1use crate::sys;
2use libc::c_void;
3use std::marker::PhantomData;
4use std::panic::catch_unwind;
5use std::process::abort;
6
7use crate::TimerSubsystem;
8
9impl TimerSubsystem {
10    #[must_use = "if unused the Timer will be dropped immediately"]
20    #[doc(alias = "SDL_AddTimer")]
21    pub fn add_timer(&self, delay: u32, callback: TimerCallback) -> Timer<'_> {
22        unsafe {
23            let mut callback = Box::new(callback);
24            let timer_id = sys::SDL_AddTimer(
25                delay,
26                Some(c_timer_callback),
27                &mut *callback as *mut TimerCallback as *mut c_void,
28            );
29
30            Timer {
31                callback: Some(callback),
32                raw: timer_id,
33                _marker: PhantomData,
34            }
35        }
36    }
37
38    #[doc(alias = "SDL_GetTicks")]
45    pub fn ticks(&self) -> u32 {
46        unsafe { sys::SDL_GetTicks() }
53    }
54
55    #[doc(alias = "SDL_GetTicks64")]
59    pub fn ticks64(&self) -> u64 {
60        unsafe { sys::SDL_GetTicks64() }
67    }
68
69    #[doc(alias = "SDL_Delay")]
73    pub fn delay(&self, ms: u32) {
74        unsafe { sys::SDL_Delay(ms) }
81    }
82
83    #[doc(alias = "SDL_GetPerformanceCounter")]
84    pub fn performance_counter(&self) -> u64 {
85        unsafe { sys::SDL_GetPerformanceCounter() }
86    }
87
88    #[doc(alias = "SDL_GetPerformanceFrequency")]
89    pub fn performance_frequency(&self) -> u64 {
90        unsafe { sys::SDL_GetPerformanceFrequency() }
91    }
92}
93
94pub type TimerCallback = Box<dyn FnMut() -> u32 + 'static + Send>;
95
96pub struct Timer<'a> {
97    callback: Option<Box<TimerCallback>>,
98    raw: sys::SDL_TimerID,
99    _marker: PhantomData<&'a ()>,
100}
101
102impl<'a> Timer<'a> {
103    pub fn into_inner(mut self) -> TimerCallback {
106        *self.callback.take().unwrap()
107    }
108}
109
110impl<'a> Drop for Timer<'a> {
111    #[inline]
112    #[doc(alias = "SDL_RemoveTimer")]
113    fn drop(&mut self) {
114        unsafe { sys::SDL_RemoveTimer(self.raw) };
118    }
119}
120
121unsafe extern "C" fn c_timer_callback(_interval: u32, param: *mut c_void) -> u32 {
122    match catch_unwind(|| {
123        let f = param.cast::<TimerCallback>();
124        unsafe { (*f)() }
125    }) {
126        Ok(ret) => ret,
127        Err(_) => abort(),
128    }
129}
130
131#[cfg(not(target_os = "macos"))]
132#[cfg(test)]
133mod test {
134    use std::sync::{Arc, Mutex};
135    use std::time::Duration;
136
137    #[test]
138    fn test_timer() {
139        test_timer_runs_multiple_times();
140        test_timer_runs_at_least_once();
141        test_timer_can_be_recreated();
142    }
143
144    fn test_timer_runs_multiple_times() {
145        let sdl_context = crate::sdl::init().unwrap();
146        let timer_subsystem = sdl_context.timer().unwrap();
147
148        let local_num = Arc::new(Mutex::new(0));
149        let timer_num = local_num.clone();
150
151        let _timer = timer_subsystem.add_timer(
152            20,
153            Box::new(move || {
154                let mut num = timer_num.lock().unwrap();
158                if *num < 9 {
159                    *num += 1;
160                    20
161                } else {
162                    0
163                }
164            }),
165        );
166
167        ::std::thread::sleep(Duration::from_millis(250));
169        let num = local_num.lock().unwrap(); assert_eq!(*num, 9); }
172
173    fn test_timer_runs_at_least_once() {
174        let sdl_context = crate::sdl::init().unwrap();
175        let timer_subsystem = sdl_context.timer().unwrap();
176
177        let local_flag = Arc::new(Mutex::new(false));
178        let timer_flag = local_flag.clone();
179
180        let _timer = timer_subsystem.add_timer(
181            20,
182            Box::new(move || {
183                let mut flag = timer_flag.lock().unwrap();
184                *flag = true;
185                0
186            }),
187        );
188
189        ::std::thread::sleep(Duration::from_millis(50));
190        let flag = local_flag.lock().unwrap();
191        assert!(*flag);
192    }
193
194    fn test_timer_can_be_recreated() {
195        let sdl_context = crate::sdl::init().unwrap();
196        let timer_subsystem = sdl_context.timer().unwrap();
197
198        let local_num = Arc::new(Mutex::new(0));
199        let timer_num = local_num.clone();
200
201        let timer_1 = timer_subsystem.add_timer(
203            20,
204            Box::new(move || {
205                let mut num = timer_num.lock().unwrap();
206                *num += 1; 0 }),
209        );
210
211        ::std::thread::sleep(Duration::from_millis(50));
213        let closure = timer_1.into_inner();
214
215        let _timer_2 = timer_subsystem.add_timer(20, closure);
217        ::std::thread::sleep(Duration::from_millis(50));
218
219        let num = local_num.lock().unwrap();
221        assert_eq!(*num, 2);
222    }
223}