use qwac_sys::core;
use std::{
marker::PhantomData,
ptr::NonNull,
time::{Duration, SystemTime},
};
pub fn time_since_epoch() -> Duration {
let secs = unsafe { core::time() };
Duration::from_secs_f64(secs)
}
pub fn now() -> SystemTime {
SystemTime::UNIX_EPOCH + time_since_epoch()
}
pub struct Timeout<F> {
id: Option<i32>,
_phantom: PhantomData<F>,
}
impl<F> Timeout<F> {
extern "C" fn callback(userdata: *mut ())
where
F: FnOnce() + 'static,
{
let func = unsafe { Box::from_raw(userdata as *mut F) };
func();
}
extern "C" fn shutdown_cleanup(userdata: *mut ())
where
F: FnOnce() + 'static,
{
unsafe {
drop(Box::from_raw(userdata as *mut F));
}
}
pub fn new(time: Duration, callback: F) -> Self
where
F: FnOnce() + 'static,
{
let id = unsafe {
core::timeout_create(
time.as_secs_f32(),
Self::callback,
Self::shutdown_cleanup,
Box::into_raw(Box::new(callback)) as *mut (),
)
};
Self {
id: Some(id),
_phantom: PhantomData,
}
}
pub fn cancel(mut self) -> Option<F> {
let userdata = unsafe { core::timeout_cancel(self.id.take().unwrap()) as *mut F };
NonNull::new(userdata).map(|userdata| *unsafe { Box::from_raw(userdata.as_ptr()) })
}
}
impl<F> Drop for Timeout<F> {
fn drop(&mut self) {
if let Some(id) = self.id {
unsafe {
core::timeout_drop(id);
}
}
}
}
pub struct Interval<F> {
id: Option<i32>,
_phantom: PhantomData<F>,
}
impl<F> Interval<F> {
extern "C" fn callback(userdata: *mut ())
where
F: FnMut() + 'static,
{
let func = unsafe { &mut *(userdata as *mut F) };
func();
}
extern "C" fn shutdown_cleanup(userdata: *mut ())
where
F: FnMut() + 'static,
{
unsafe {
drop(Box::from_raw(userdata as *mut F));
}
}
pub fn new(time: Duration, callback: F) -> Self
where
F: FnMut() + 'static,
{
let id = unsafe {
core::interval_create(
time.as_secs_f32(),
Self::callback,
Self::shutdown_cleanup,
Box::into_raw(Box::new(callback)) as *mut (),
)
};
Self {
id: Some(id),
_phantom: PhantomData,
}
}
pub fn cancel(mut self) -> F {
let userdata = unsafe { core::interval_cancel(self.id.take().unwrap()) as *mut F };
let userdata = NonNull::new(userdata).expect("interval cancellation should never be null");
*unsafe { Box::from_raw(userdata.as_ptr()) }
}
}
impl<F> Drop for Interval<F> {
fn drop(&mut self) {
if let Some(id) = self.id {
unsafe {
core::interval_drop(id);
}
}
}
}