qwac 0.29.0

Rust client crate for making qwac games
Documentation
use qwac_sys::core;
use std::{
    marker::PhantomData,
    ptr::NonNull,
    time::{Duration, SystemTime},
};

/// The current time as a Duration from the unix epoch.
pub fn time_since_epoch() -> Duration {
    let secs = unsafe { core::time() };
    Duration::from_secs_f64(secs)
}

/// The current time.
pub fn now() -> SystemTime {
    SystemTime::UNIX_EPOCH + time_since_epoch()
}

/// Sets up a function to be called after a timeout expires.
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);
            }
        }
    }
}

/// Sets up a function to be called on an interval.
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);
            }
        }
    }
}