use core::ffi::c_void;
use core::ffi::c_int;
use core::marker::PhantomData;
use core::pin::Pin;
use alloc::boxed::Box;
use crate::api;
use crate::System;
pub struct Handler<'t, F, U>(Option<Pin<Box<(F, U)>>>, PhantomData<&'t ()>);
impl<'t, F, U> Drop for Handler<'t, F, U> {
fn drop(&mut self) {
let get_fn = || sys::api_opt!(system.setUpdateCallback);
if self.0.is_some() {
if let Some(f) = get_fn() {
unsafe {
f(None, core::ptr::null_mut());
}
}
}
}
}
impl<Api: api::Api> System<Api> {
unsafe extern "C" fn proxy<UD, Fn: FnMut(&mut UD) -> UpdateCtrl>(fn_ud: *mut c_void) -> c_int {
if let Some((callback, userdata)) = (fn_ud as *mut (Fn, UD)).as_mut() {
callback(userdata).into()
} else {
panic!("user callback missed");
}
}
#[doc(alias = "sys::ffi::playdate_sys::setUpdateCallback")]
#[must_use = "Update handler will be unregistered when Handler dropped"]
pub fn set_update_callback<'u, U, F>(&self, on_update: F, userdata: U) -> Handler<'u, F, U>
where U: 'u,
F: 'u + FnMut(&mut U) -> UpdateCtrl {
let f = self.0.set_update_callback();
let mut userdata = Box::pin((on_update, userdata));
let ptr = unsafe { userdata.as_mut().get_unchecked_mut() } as *mut _ as *mut c_void;
unsafe { f(Some(Self::proxy::<U, F>), ptr) };
Handler(userdata.into(), PhantomData)
}
#[doc(alias = "sys::ffi::playdate_sys::setUpdateCallback")]
pub fn set_update_callback_boxed<'u, U, F>(&self, on_update: F, userdata: U)
where U: 'u,
F: 'u + FnMut(&mut U) -> UpdateCtrl {
let f = self.0.set_update_callback();
let ptr = Box::into_raw(Box::new((on_update, userdata)));
unsafe { f(Some(Self::proxy::<U, F>), ptr as *mut _) };
}
#[doc(alias = "sys::ffi::playdate_sys::setUpdateCallback")]
pub fn set_update_callback_static<U: 'static>(&self,
on_update: Option<fn(userdata: &mut U) -> UpdateCtrl>,
userdata: U) {
unsafe extern "C" fn proxy<UD: 'static>(fn_ud: *mut c_void) -> c_int {
if let Some((callback, userdata)) = (fn_ud as *mut (fn(userdata: &mut UD) -> UpdateCtrl, UD)).as_mut() {
callback(userdata).into()
} else {
panic!("user callback missed");
}
}
let f = self.0.set_update_callback();
if let Some(callback) = on_update {
let ptr = Box::into_raw(Box::new((callback, userdata)));
unsafe { f(Some(proxy::<U>), ptr as *mut _) };
} else {
unsafe { f(None, core::ptr::null_mut()) };
}
}
#[doc(alias = "sys::ffi::playdate_sys::setUpdateCallback")]
#[inline(always)]
pub fn set_update_handler<'t, U: 'static + Update>(&'t self, handler: Option<&'static mut U>)
where &'t Api: api::Api {
if let Some(handler) = handler {
handler.set_update_handler_with(&self.0)
} else {
let f = self.0.set_update_callback();
unsafe { f(None, core::ptr::null_mut()) };
}
}
}
pub trait Update: Sized {
fn update(&mut self) -> UpdateCtrl;
#[doc(alias = "sys::ffi::playdate_sys::setUpdateCallback")]
#[inline(always)]
fn set_update_handler(&'static mut self) { self.set_update_handler_with(api::Default) }
#[doc(alias = "sys::ffi::playdate_sys::setUpdateCallback")]
fn set_update_handler_with<Api: api::Api>(&'static mut self, api: Api) {
let f = api.set_update_callback();
unsafe { f(Some(Self::update_proxy), self as *mut Self as *mut _) };
}
#[doc(hidden)]
unsafe extern "C" fn update_proxy(handler: *mut c_void) -> c_int {
if let Some(handler) = (handler as *mut Self).as_mut() {
Self::update(handler).into()
} else {
panic!("user callback missed");
}
}
}
#[repr(i32)]
pub enum UpdateCtrl {
Stop = 0,
Continue = 1,
}
impl UpdateCtrl {
pub const fn into(self) -> c_int { self as _ }
}
impl From<bool> for UpdateCtrl {
fn from(value: bool) -> Self { if value { Self::Continue } else { Self::Stop } }
}
impl<T, E> From<Result<T, E>> for UpdateCtrl {
fn from(res: Result<T, E>) -> Self { if res.is_ok() { Self::Continue } else { Self::Stop } }
}
#[cfg(feature = "try-trait-v2")]
mod impl_trait_v2 {
use super::*;
use core::convert::Infallible;
use core::ops::FromResidual;
use core::fmt::Display;
impl<E: Display> FromResidual<Result<Infallible, E>> for UpdateCtrl {
#[track_caller]
fn from_residual(residual: Result<Infallible, E>) -> Self {
if let Err(err) = residual {
sys::println!("Error: {err}");
Self::Stop
} else {
Self::Continue
}
}
}
}