1use crate::sys;
2use libc::c_void;
3use std::ptr::NonNull;
4
5#[must_use = "if unused the Timer will be dropped immediately"]
15#[doc(alias = "SDL_AddTimer")]
16pub fn add_timer(delay: u32, callback: TimerCallback) -> Timer {
17 unsafe {
18 let callback_ptr = Box::into_raw(Box::new(callback));
20
21 let timer_id =
23 sys::timer::SDL_AddTimer(delay, Some(c_timer_callback), callback_ptr as *mut c_void);
24
25 Timer {
26 callback: Some(NonNull::new(callback_ptr).unwrap()),
27 raw: timer_id,
28 }
29 }
30}
31
32#[doc(alias = "SDL_GetTicks")]
36pub fn ticks() -> u64 {
37 unsafe { sys::timer::SDL_GetTicks() }
38}
39
40#[doc(alias = "SDL_Delay")]
44pub fn delay(ms: u32) {
45 unsafe { sys::timer::SDL_Delay(ms) }
46}
47
48#[doc(alias = "SDL_GetPerformanceCounter")]
49pub fn performance_counter() -> u64 {
50 unsafe { sys::timer::SDL_GetPerformanceCounter() }
51}
52
53#[doc(alias = "SDL_GetPerformanceFrequency")]
54pub fn performance_frequency() -> u64 {
55 unsafe { sys::timer::SDL_GetPerformanceFrequency() }
56}
57
58pub type TimerCallback = Box<dyn FnMut() -> u32 + Send + 'static>;
60
61pub struct Timer {
62 callback: Option<NonNull<TimerCallback>>,
63 raw: sys::timer::SDL_TimerID,
64}
65
66impl Timer {
67 pub fn into_inner(mut self) -> TimerCallback {
70 unsafe {
71 sys::timer::SDL_RemoveTimer(self.raw);
72 if let Some(callback_ptr) = self.callback.take() {
73 Box::from_raw(callback_ptr.as_ptr())
75 } else {
76 panic!("Timer callback already taken");
77 }
78 }
79 }
80}
81
82impl Drop for Timer {
83 #[inline]
84 #[doc(alias = "SDL_RemoveTimer")]
85 fn drop(&mut self) {
86 unsafe {
87 sys::timer::SDL_RemoveTimer(self.raw);
88 if let Some(callback_ptr) = self.callback.take() {
89 let _ = Box::from_raw(callback_ptr.as_ptr());
91 }
92 }
93 }
94}
95
96extern "C" fn c_timer_callback(
97 userdata: *mut c_void,
98 _timer_id: sys::timer::SDL_TimerID,
99 _interval: u32,
100) -> u32 {
101 let callback_ptr = userdata as *mut TimerCallback;
102 unsafe { (*callback_ptr)() }
103}
104
105#[cfg(not(target_os = "macos"))]
106#[cfg(test)]
107mod test {
108 use std::sync::{Arc, Mutex};
109 use std::time::Duration;
110
111 use crate::timer::add_timer;
112
113 #[test]
114 fn test_timer_runs_multiple_times() {
115 let _sdl_context = crate::sdl::init().unwrap();
116 let local_num = Arc::new(Mutex::new(0));
119 let timer_num = local_num.clone();
120
121 let _timer = add_timer(
122 20,
123 Box::new(move || {
124 let mut num = timer_num.lock().unwrap();
125 if *num < 9 {
126 *num += 1;
127 20
128 } else {
129 0
130 }
131 }),
132 );
133
134 std::thread::sleep(Duration::from_millis(250));
135 let num = local_num.lock().unwrap();
136 assert_eq!(*num, 9);
137 }
138
139 #[test]
140 fn test_timer_runs_at_least_once() {
141 let _sdl_context = crate::sdl::init().unwrap();
142 let local_flag = Arc::new(Mutex::new(false));
145 let timer_flag = local_flag.clone();
146
147 let _timer = add_timer(
148 20,
149 Box::new(move || {
150 let mut flag = timer_flag.lock().unwrap();
151 *flag = true;
152 0
153 }),
154 );
155
156 std::thread::sleep(Duration::from_millis(50));
157 let flag = local_flag.lock().unwrap();
158 assert_eq!(*flag, true);
159 }
160
161 #[test]
162 fn test_timer_can_be_recreated() {
163 let sdl_context = crate::sdl::init().unwrap();
164 let local_num = Arc::new(Mutex::new(0));
167 let timer_num = local_num.clone();
168
169 let timer_1 = add_timer(
171 20,
172 Box::new(move || {
173 let mut num = timer_num.lock().unwrap();
174 *num += 1;
175 0
176 }),
177 );
178
179 std::thread::sleep(Duration::from_millis(50));
181 let closure = timer_1.into_inner();
182
183 let _timer_2 = add_timer(20, closure);
185 std::thread::sleep(Duration::from_millis(50));
186
187 let num = local_num.lock().unwrap();
189 assert_eq!(*num, 2);
190 }
191}