playdate_system/
update.rs1use core::ffi::c_void;
2use core::ffi::c_int;
3use core::marker::PhantomData;
4use core::pin::Pin;
5use alloc::boxed::Box;
6
7use crate::api;
8use crate::System;
9
10
11pub struct Handler<'t, F, U>(Option<Pin<Box<(F, U)>>>, PhantomData<&'t ()>);
15
16impl<'t, F, U> Drop for Handler<'t, F, U> {
17 fn drop(&mut self) {
18 let get_fn = || sys::api_opt!(system.setUpdateCallback);
19 if self.0.is_some() {
20 if let Some(f) = get_fn() {
21 unsafe {
22 f(None, core::ptr::null_mut());
23 }
24 }
25 }
26 }
27}
28
29
30impl<Api: api::Api> System<Api> {
31 unsafe extern "C" fn proxy<UD, Fn: FnMut(&mut UD) -> UpdateCtrl>(fn_ud: *mut c_void) -> c_int {
33 if let Some((callback, userdata)) = (fn_ud as *mut (Fn, UD)).as_mut() {
34 callback(userdata).into()
35 } else {
36 panic!("user callback missed");
37 }
38 }
39
40
41 #[doc(alias = "sys::ffi::playdate_sys::setUpdateCallback")]
52 #[must_use = "Update handler will be unregistered when Handler dropped"]
53 pub fn set_update_callback<'u, U, F>(&self, on_update: F, userdata: U) -> Handler<'u, F, U>
54 where U: 'u,
55 F: 'u + FnMut(&mut U) -> UpdateCtrl {
56 let f = self.0.set_update_callback();
57 let mut userdata = Box::pin((on_update, userdata));
58 let ptr = unsafe { userdata.as_mut().get_unchecked_mut() } as *mut _ as *mut c_void;
59 unsafe { f(Some(Self::proxy::<U, F>), ptr) };
60 Handler(userdata.into(), PhantomData)
61 }
62
63 #[doc(alias = "sys::ffi::playdate_sys::setUpdateCallback")]
74 pub fn set_update_callback_boxed<'u, U, F>(&self, on_update: F, userdata: U)
75 where U: 'u,
76 F: 'u + FnMut(&mut U) -> UpdateCtrl {
77 let f = self.0.set_update_callback();
78 let ptr = Box::into_raw(Box::new((on_update, userdata)));
79 unsafe { f(Some(Self::proxy::<U, F>), ptr as *mut _) };
80 }
81
82
83 #[doc(alias = "sys::ffi::playdate_sys::setUpdateCallback")]
90 pub fn set_update_callback_static<U: 'static>(&self,
91 on_update: Option<fn(userdata: &mut U) -> UpdateCtrl>,
92 userdata: U) {
93 unsafe extern "C" fn proxy<UD: 'static>(fn_ud: *mut c_void) -> c_int {
94 if let Some((callback, userdata)) = (fn_ud as *mut (fn(userdata: &mut UD) -> UpdateCtrl, UD)).as_mut() {
95 callback(userdata).into()
96 } else {
97 panic!("user callback missed");
98 }
99 }
100
101 let f = self.0.set_update_callback();
102 if let Some(callback) = on_update {
103 let ptr = Box::into_raw(Box::new((callback, userdata)));
104 unsafe { f(Some(proxy::<U>), ptr as *mut _) };
105 } else {
106 unsafe { f(None, core::ptr::null_mut()) };
107 }
108 }
109
110 #[doc(alias = "sys::ffi::playdate_sys::setUpdateCallback")]
114 #[inline(always)]
115 pub fn set_update_handler<'t, U: 'static + Update>(&'t self, handler: Option<&'static mut U>)
116 where &'t Api: api::Api {
117 if let Some(handler) = handler {
118 handler.set_update_handler_with(&self.0)
119 } else {
120 let f = self.0.set_update_callback();
121 unsafe { f(None, core::ptr::null_mut()) };
122 }
123 }
124}
125
126
127pub trait Update: Sized {
130 fn update(&mut self) -> UpdateCtrl;
131
132 #[doc(alias = "sys::ffi::playdate_sys::setUpdateCallback")]
139 #[inline(always)]
140 fn set_update_handler(&'static mut self) { self.set_update_handler_with(api::Default) }
141
142 #[doc(alias = "sys::ffi::playdate_sys::setUpdateCallback")]
147 fn set_update_handler_with<Api: api::Api>(&'static mut self, api: Api) {
148 let f = api.set_update_callback();
149 unsafe { f(Some(Self::update_proxy), self as *mut Self as *mut _) };
150 }
151
152
153 #[doc(hidden)]
155 unsafe extern "C" fn update_proxy(handler: *mut c_void) -> c_int {
156 if let Some(handler) = (handler as *mut Self).as_mut() {
157 Self::update(handler).into()
158 } else {
159 panic!("user callback missed");
160 }
161 }
162}
163
164
165#[repr(i32)]
166pub enum UpdateCtrl {
167 Stop = 0,
168 Continue = 1,
169}
170
171impl UpdateCtrl {
172 pub const fn into(self) -> c_int { self as _ }
173}
174
175impl From<bool> for UpdateCtrl {
176 fn from(value: bool) -> Self { if value { Self::Continue } else { Self::Stop } }
177}
178
179impl<T, E> From<Result<T, E>> for UpdateCtrl {
180 fn from(res: Result<T, E>) -> Self { if res.is_ok() { Self::Continue } else { Self::Stop } }
181}
182
183#[cfg(feature = "try-trait-v2")]
184mod impl_trait_v2 {
185 use super::*;
186 use core::convert::Infallible;
187 use core::ops::FromResidual;
188 use core::fmt::Display;
189
190 impl<E: Display> FromResidual<Result<Infallible, E>> for UpdateCtrl {
191 #[track_caller]
192 fn from_residual(residual: Result<Infallible, E>) -> Self {
193 let Err(err) = residual;
194 sys::println!("Error: {err}");
195 Self::Stop
197 }
198 }
199}