playdate_system/
update.rs

1use 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
11/// Pinned wrapper around a function and user data.
12///
13/// On drop, automatically resets system registered update handler.
14pub 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	/// Internal update callback proxy function.
32	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	/// Takes __any__ function and `userdata`,
42	/// registers callback in the system and
43	/// returns this function with userdata wrapped into the [`Handler`] with [`Pin`] inside.
44	///
45	/// For register a fn-ptr you could better use [`set_update_callback_static`].
46	///
47	/// Safety is ensured by [`Handler`],
48	/// that resets the system registered update handler when drop.
49	///
50	/// Wrapping [`sys::ffi::playdate_sys::setUpdateCallback`]
51	#[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	/// Consumes and __leaks__ an __any__ function with `userdata` into the `Box`,
64	/// registers callback in the system.
65	///
66	/// For register a fn-ptr you could better use [`set_update_callback_static`].
67	///
68	/// __Safety is guaranteed by the caller.__
69	///
70	/// See also [`System::set_update_callback`], it prevents leaks and more safe.
71	///
72	/// Wrapping [`sys::ffi::playdate_sys::setUpdateCallback`]
73	#[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	/// Consumes and __leaks__ function `on_update` and `userdata`, wraps it into the `Box`,
84	/// then registers callback.
85	///
86	/// See also [`System::set_update_callback`], it prevents leaks and more safe.
87	///
88	/// Wrapping [`sys::ffi::playdate_sys::setUpdateCallback`]
89	#[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	/// Executes `handler`'s [`Update::set_update_handler_with`] with this inner api.
111	///
112	/// Wrapping [`sys::ffi::playdate_sys::setUpdateCallback`]
113	#[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
127/// Implementable stateful update handler
128/// with default implementation for adapter and register functions.
129pub trait Update: Sized {
130	fn update(&mut self) -> UpdateCtrl;
131
132	/// Register a callback function [`Self::update`] in the system,
133	/// using [`Default`](api::Default) `api`.
134	///
135	/// See also [`Update::set_update_handler_with`] and [`System::set_update_handler`].
136	///
137	/// Equivalent to [`sys::ffi::playdate_sys::setUpdateCallback`]
138	#[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	/// Register a callback function [`Self::update`] in the system,
143	/// using given `api`.
144	///
145	/// Equivalent to [`sys::ffi::playdate_sys::setUpdateCallback`]
146	#[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	/// Overridable update callback adapter.
154	#[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			if let Err(err) = residual {
194				sys::println!("Error: {err}");
195				// panic!("{err}");
196				Self::Stop
197			} else {
198				Self::Continue
199			}
200		}
201	}
202}