playdate
playdate copied to clipboard
invent analog of `std::time::Instant`
https://api.github.com/boozook/playdate/blob/1da2ad4a2e474f3bfca7d51fcb56de8e695a3210/api/system/src/lib.rs#L170
#![cfg_attr(not(test), no_std)]
extern crate sys;
extern crate alloc;
use core::ffi::c_float;
use core::ffi::c_int;
use core::ffi::c_uint;
use core::ffi::c_void;
use core::marker::PhantomData;
use core::pin::Pin;
use core::time::Duration;
pub mod time;
pub mod lang;
pub use time::*;
pub use lang::*;
#[derive(Debug, Clone, Copy)]
pub struct System<Api = api::Default>(Api);
impl<Api: Default + api::Api> Default for System<Api> {
fn default() -> Self { Self(Default::default()) }
}
impl<Api: Default + api::Api> System<Api> {
pub fn new(api: Api) -> Self { Self(api) }
}
impl<Api: api::Api> System<Api> {
pub fn new_with(api: Api) -> Self { Self(api) }
}
pub struct CallbackHandler<'t, F, U>(Option<Pin<Box<(F, U)>>>, PhantomData<&'t ()>);
impl<'t, F, U> Drop for CallbackHandler<'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> {
/// Takes an any function with `userdata` into the `Pin`,
/// registers callback in the system and returns this wrapped function with userdata.
///
/// For register a fn-ptr you could better use [`set_update_callback_static`].
///
/// Wrapping [`sys::ffi::playdate_sys::setUpdateCallback`]
#[doc(alias = "sys::ffi::playdate_sys::setUpdateCallback")]
pub fn set_update_callback<'u, U, F>(&self, on_update: F, userdata: U) -> CallbackHandler<'u, F, U>
where U: 'u,
F: 'u + FnMut(&mut U) -> bool {
unsafe extern "C" fn proxy<UD, Fn: FnMut(&mut UD) -> bool>(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");
}
}
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(proxy::<U, F>), ptr) };
CallbackHandler(userdata.into(), PhantomData)
}
/// Takes `on_update` and `userdata` and wraps it into the `Box`,
/// then registers callback.
///
/// Wrapping [`sys::ffi::playdate_sys::setUpdateCallback`]
#[doc(alias = "sys::ffi::playdate_sys::setUpdateCallback")]
pub fn set_update_callback_static<U: 'static>(&self,
on_update: Option<fn(userdata: &mut U) -> bool>,
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) -> bool, 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()) };
}
}
}
impl<Api: api::Api> System<Api> {
/// Equivalent to [`sys::ffi::playdate_sys::getLanguage`]
#[doc(alias = "sys::ffi::playdate_sys::getLanguage")]
#[inline(always)]
pub fn language(&self) -> PDLanguage {
let f = self.0.get_language();
unsafe { f() }
}
/// Equivalent to [`sys::ffi::playdate_sys::getCurrentTimeMilliseconds`]
#[doc(alias = "sys::ffi::playdate_sys::getCurrentTimeMilliseconds")]
#[inline(always)]
pub fn current_time_milliseconds(&self) -> Duration {
let f = self.0.get_current_time_milliseconds();
let t = unsafe { f() };
Duration::from_millis(t.into())
}
/// Equivalent to [`sys::ffi::playdate_sys::getSecondsSinceEpoch`]
#[doc(alias = "sys::ffi::playdate_sys::getSecondsSinceEpoch")]
#[inline(always)]
pub fn seconds_since_epoch(&self) -> Duration {
let f = self.0.get_seconds_since_epoch();
let mut millis: c_uint = 0;
let secs = unsafe { f(&mut millis) };
Duration::new(secs.into(), 0) + Duration::from_millis(millis.into())
}
/// Equivalent to [`sys::ffi::playdate_sys::drawFPS`]
#[doc(alias = "sys::ffi::playdate_sys::drawFPS")]
#[inline(always)]
pub fn draw_fps(&self, x: c_int, y: c_int) {
let f = self.0.draw_fps();
unsafe { f(x, y) }
}
/// Equivalent to [`sys::ffi::playdate_sys::getFlipped`]
#[doc(alias = "sys::ffi::playdate_sys::getFlipped")]
#[inline(always)]
pub fn flipped(&self) -> bool {
let f = self.0.get_flipped();
unsafe { f() == 1 }
}
/// Equivalent to [`sys::ffi::playdate_sys::setAutoLockDisabled`]
#[doc(alias = "sys::ffi::playdate_sys::setAutoLockDisabled")]
#[inline(always)]
pub fn set_auto_lock_disabled(&self, disable: bool) {
let f = self.0.set_auto_lock_disabled();
unsafe { f(disable as _) }
}
/// Equivalent to [`sys::ffi::playdate_sys::getReduceFlashing`]
#[doc(alias = "sys::ffi::playdate_sys::getReduceFlashing")]
#[inline(always)]
pub fn reduce_flashing(&self) -> bool {
let f = self.0.get_reduce_flashing();
unsafe { f() == 1 }
}
// TODO: invent analog of `std::time::Instant`
/// Equivalent to [`sys::ffi::playdate_sys::getElapsedTime`]
#[doc(alias = "sys::ffi::playdate_sys::getElapsedTime")]
#[inline(always)]
pub fn elapsed_time(&self) -> Duration {
let f = self.0.get_elapsed_time();
let secs = unsafe { f() };
Duration::from_secs_f32(secs)
}
/// Equivalent to [`sys::ffi::playdate_sys::resetElapsedTime`]
#[doc(alias = "sys::ffi::playdate_sys::resetElapsedTime")]
#[inline(always)]
pub fn reset_elapsed_time(&self) {
let f = self.0.reset_elapsed_time();
unsafe { f() }
}
/// Equivalent to [`sys::ffi::playdate_sys::getBatteryPercentage`]
#[doc(alias = "sys::ffi::playdate_sys::getBatteryPercentage")]
#[inline(always)]
pub fn battery_percentage(&self) -> c_float {
let f = self.0.get_battery_percentage();
unsafe { f() }
}
/// Equivalent to [`sys::ffi::playdate_sys::getBatteryVoltage`]
#[doc(alias = "sys::ffi::playdate_sys::getBatteryVoltage")]
#[inline(always)]
pub fn battery_voltage(&self) -> c_float {
let f = self.0.get_battery_voltage();
unsafe { f() }
}
/// Equivalent to [`sys::ffi::playdate_sys::getTimezoneOffset`]
#[doc(alias = "sys::ffi::playdate_sys::getTimezoneOffset")]
#[inline(always)]
pub fn timezone_offset(&self) -> i32 {
let f = self.0.get_timezone_offset();
unsafe { f() }
}
/// Equivalent to [`sys::ffi::playdate_sys::shouldDisplay24HourTime`]
#[doc(alias = "sys::ffi::playdate_sys::shouldDisplay24HourTime")]
#[inline(always)]
pub fn should_display_24_hour_time(&self) -> bool {
let f = self.0.should_display_24_hour_time();
unsafe { f() == 1 }
}
/// Equivalent to [`sys::ffi::playdate_sys::convertEpochToDateTime`]
#[doc(alias = "sys::ffi::playdate_sys::convertEpochToDateTime")]
#[inline(always)]
pub fn convert_epoch_to_date_time(&self, epoch: u32) -> PDDateTime {
let mut dt = PDDateTime { year: 0,
month: 0,
day: 0,
weekday: 0,
hour: 0,
minute: 0,
second: 0 };
self.convert_epoch_to_date_time_to(epoch, &mut dt);
dt
}
/// Equivalent to [`sys::ffi::playdate_sys::convertEpochToDateTime`]
#[doc(alias = "sys::ffi::playdate_sys::convertEpochToDateTime")]
#[inline(always)]
pub fn convert_epoch_to_date_time_to(&self, epoch: u32, dt: &mut PDDateTime) {
let f = self.0.convert_epoch_to_date_time();
unsafe { f(epoch, dt) }
}
/// Equivalent to [`sys::ffi::playdate_sys::convertDateTimeToEpoch`]
#[doc(alias = "sys::ffi::playdate_sys::convertDateTimeToEpoch")]
pub fn convert_date_time_to_epoch(&self, dt: &PDDateTime) -> u32 {
let f = self.0.convert_date_time_to_epoch();
let epoch = unsafe { f(dt as *const _ as *mut _) };
let _ = dt; // this to prevent earlier drop.
epoch
}
}
pub mod api {
use core::ffi::c_float;
use core::ffi::c_int;
use core::ffi::c_uint;
use core::ffi::c_void;
use sys::ffi::PDCallbackFunction;
use sys::ffi::PDDateTime;
use sys::ffi::PDLanguage;
#[derive(Debug, Clone, Copy, core::default::Default)]
pub struct Default;
impl Api for Default {}
pub trait Api {
/// Equivalent to [`sys::ffi::playdate_sys::getLanguage`]
#[doc(alias = "sys::ffi::playdate_sys::getLanguage")]
#[inline(always)]
fn get_language(&self) -> unsafe extern "C" fn() -> PDLanguage { *sys::api!(system.getLanguage) }
/// Equivalent to [`sys::ffi::playdate_sys::getCurrentTimeMilliseconds`]
#[doc(alias = "sys::ffi::playdate_sys::getCurrentTimeMilliseconds")]
#[inline(always)]
fn get_current_time_milliseconds(&self) -> unsafe extern "C" fn() -> c_uint {
*sys::api!(system.getCurrentTimeMilliseconds)
}
/// Equivalent to [`sys::ffi::playdate_sys::getSecondsSinceEpoch`]
#[doc(alias = "sys::ffi::playdate_sys::getSecondsSinceEpoch")]
#[inline(always)]
fn get_seconds_since_epoch(&self) -> unsafe extern "C" fn(milliseconds: *mut c_uint) -> c_uint {
*sys::api!(system.getSecondsSinceEpoch)
}
/// Equivalent to [`sys::ffi::playdate_sys::drawFPS`]
#[doc(alias = "sys::ffi::playdate_sys::drawFPS")]
#[inline(always)]
fn draw_fps(&self) -> unsafe extern "C" fn(x: c_int, y: c_int) { *sys::api!(system.drawFPS) }
/// Equivalent to [`sys::ffi::playdate_sys::setUpdateCallback`]
#[doc(alias = "sys::ffi::playdate_sys::setUpdateCallback")]
#[inline(always)]
fn set_update_callback(&self) -> unsafe extern "C" fn(update: PDCallbackFunction, userdata: *mut c_void) {
*sys::api!(system.setUpdateCallback)
}
/// Equivalent to [`sys::ffi::playdate_sys::getFlipped`]
#[doc(alias = "sys::ffi::playdate_sys::getFlipped")]
#[inline(always)]
fn get_flipped(&self) -> unsafe extern "C" fn() -> c_int { *sys::api!(system.getFlipped) }
/// Equivalent to [`sys::ffi::playdate_sys::setAutoLockDisabled`]
#[doc(alias = "sys::ffi::playdate_sys::setAutoLockDisabled")]
#[inline(always)]
fn set_auto_lock_disabled(&self) -> unsafe extern "C" fn(disable: c_int) {
*sys::api!(system.setAutoLockDisabled)
}
/// Equivalent to [`sys::ffi::playdate_sys::getReduceFlashing`]
#[doc(alias = "sys::ffi::playdate_sys::getReduceFlashing")]
#[inline(always)]
fn get_reduce_flashing(&self) -> unsafe extern "C" fn() -> c_int { *sys::api!(system.getReduceFlashing) }
/// Equivalent to [`sys::ffi::playdate_sys::getElapsedTime`]
#[doc(alias = "sys::ffi::playdate_sys::getElapsedTime")]
#[inline(always)]
fn get_elapsed_time(&self) -> unsafe extern "C" fn() -> c_float { *sys::api!(system.getElapsedTime) }
/// Equivalent to [`sys::ffi::playdate_sys::resetElapsedTime`]
#[doc(alias = "sys::ffi::playdate_sys::resetElapsedTime")]
#[inline(always)]
fn reset_elapsed_time(&self) -> unsafe extern "C" fn() { *sys::api!(system.resetElapsedTime) }
/// Equivalent to [`sys::ffi::playdate_sys::getBatteryPercentage`]
#[doc(alias = "sys::ffi::playdate_sys::getBatteryPercentage")]
#[inline(always)]
fn get_battery_percentage(&self) -> unsafe extern "C" fn() -> c_float {
*sys::api!(system.getBatteryPercentage)
}
/// Equivalent to [`sys::ffi::playdate_sys::getBatteryVoltage`]
#[doc(alias = "sys::ffi::playdate_sys::getBatteryVoltage")]
#[inline(always)]
fn get_battery_voltage(&self) -> unsafe extern "C" fn() -> c_float { *sys::api!(system.getBatteryVoltage) }
/// Equivalent to [`sys::ffi::playdate_sys::getTimezoneOffset`]
#[doc(alias = "sys::ffi::playdate_sys::getTimezoneOffset")]
#[inline(always)]
fn get_timezone_offset(&self) -> unsafe extern "C" fn() -> i32 { *sys::api!(system.getTimezoneOffset) }
/// Equivalent to [`sys::ffi::playdate_sys::shouldDisplay24HourTime`]
#[doc(alias = "sys::ffi::playdate_sys::shouldDisplay24HourTime")]
#[inline(always)]
fn should_display_24_hour_time(&self) -> unsafe extern "C" fn() -> c_int {
*sys::api!(system.shouldDisplay24HourTime)
}
/// Equivalent to [`sys::ffi::playdate_sys::convertEpochToDateTime`]
#[doc(alias = "sys::ffi::playdate_sys::convertEpochToDateTime")]
#[inline(always)]
fn convert_epoch_to_date_time(&self) -> unsafe extern "C" fn(epoch: u32, datetime: *mut PDDateTime) {
*sys::api!(system.convertEpochToDateTime)
}
/// Equivalent to [`sys::ffi::playdate_sys::convertDateTimeToEpoch`]
#[doc(alias = "sys::ffi::playdate_sys::convertDateTimeToEpoch")]
#[inline(always)]
fn convert_date_time_to_epoch(&self) -> unsafe extern "C" fn(datetime: *mut PDDateTime) -> u32 {
*sys::api!(system.convertDateTimeToEpoch)
}
}
}