rich_sdl2_rust/
timer.rs

1use std::{ffi::c_void, marker::PhantomData, num::NonZeroU32, ops, ptr::addr_of_mut};
2
3use crate::{bind, Result, Sdl, SdlError};
4
5mod ticks;
6
7pub use ticks::*;
8
9/// A callback for [`Timer`], that returns an interval for next calling.
10pub trait TimerCallback<'callback>: FnMut() -> u32 + 'callback {}
11
12/// A timer invokes a [`TimerCallback`] with the interval.
13pub struct Timer<'sdl, T> {
14    id: NonZeroU32,
15    callback: T,
16    _phantom: PhantomData<&'sdl Sdl>,
17}
18
19impl<T> std::fmt::Debug for Timer<'_, T> {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        f.debug_struct("Timer")
22            .field("id", &self.id)
23            .finish_non_exhaustive()
24    }
25}
26
27impl<'sdl, 'callback, T: TimerCallback<'callback>> Timer<'sdl, T> {
28    /// Constructs a timer with initial interval and callback.
29    ///
30    /// The timing may be inaccurate because of OS scheduling. Make sure to check the current time in your callback.
31    ///
32    /// # Errors
33    ///
34    /// Returns `Err` if failed to create a new timer.
35    pub fn new(sdl: &'sdl Sdl, interval: u32, mut callback: T) -> Result<Self> {
36        let ret = unsafe { bind::SDL_InitSubSystem(bind::SDL_INIT_TIMER) };
37        if ret != 0 {
38            Sdl::error_then_panic("Sdl timer");
39        }
40
41        let data = addr_of_mut!(callback);
42        let id =
43            unsafe { bind::SDL_AddTimer(interval, Some(timer_wrap_handler::<T>), data.cast()) };
44        if id == 0 {
45            Err(SdlError::Others { msg: Sdl::error() })
46        } else {
47            Ok(Self {
48                id: unsafe { NonZeroU32::new_unchecked(id as u32) },
49                callback,
50                _phantom: PhantomData,
51            })
52        }
53    }
54}
55
56extern "C" fn timer_wrap_handler<'callback, T: TimerCallback<'callback>>(
57    _: u32,
58    param: *mut c_void,
59) -> u32 {
60    let callback = unsafe { &mut *param.cast::<T>() };
61    callback()
62}
63
64impl<'sdl, T> Drop for Timer<'sdl, T> {
65    fn drop(&mut self) {
66        unsafe {
67            let _ = bind::SDL_RemoveTimer(self.id.get() as bind::SDL_TimerID);
68            bind::SDL_QuitSubSystem(bind::SDL_INIT_TIMER);
69        }
70    }
71}
72
73/// Stops the current thread for `ms` milliseconds, then returns.
74pub fn delay(ms: u32) {
75    unsafe { bind::SDL_Delay(ms) }
76}
77
78/// A counter for performance analysis.
79pub mod performance {
80    use crate::bind;
81
82    /// Returns current counts of the high resolution counter.
83    #[must_use]
84    pub fn counter() -> u64 {
85        unsafe { bind::SDL_GetPerformanceCounter() }
86    }
87
88    /// Returns the numbers of counts per one seconds of the high resolution counter.
89    #[must_use]
90    pub fn frequency() -> u64 {
91        unsafe { bind::SDL_GetPerformanceFrequency() }
92    }
93}