native_windows_gui/controls/
animation_timer.rs1use crate::controls::ControlHandle;
2use crate::NwgError;
3use crate::win32::window_helper as wh;
4use std::{thread, time::{Duration, Instant}, sync::{Mutex, Arc}};
5
6use winapi::um::winuser::SendNotifyMessageW;
7use winapi::shared::minwindef::{WPARAM, LPARAM};
8use winapi::shared::windef::HWND;
9
10const NOT_BOUND: &'static str = "AnimationTimer is not yet bound to a winapi object";
11const BAD_HANDLE: &'static str = "INTERNAL ERROR: AnimationTimer handle is not Timer!";
12
13lazy_static! {
14
15 static ref THREAD_STATE: Arc<Mutex<AnimationThread>> = {
16 let state = AnimationThread {
17 timers: Vec::new(),
18 };
19
20 let state = Arc::new(Mutex::new(state));
21 let shared_state = state.clone();
22
23 thread::spawn(move || {
24 let sleep_time = Duration::from_millis(1);
25
26 loop {
27 let mut state = shared_state.lock().unwrap();
28
29 for (id, timer) in state.timers.iter_mut().enumerate() {
30 let timer = match timer.as_mut() {
31 Some(t) => match t.active {
32 true => t,
33 false => { continue; }
34 },
35 None => { continue; }
36 };
37
38 if timer.last_tick.elapsed() > timer.interval {
39 AnimationThread::timer_tick(id as u32, timer.hwnd);
40 timer.last_tick = Instant::now();
41 timer.current_tick += 1;
42
43 if Some(timer.current_tick) == timer.max_tick {
44 AnimationThread::timer_stop(id as u32, timer.hwnd);
45 timer.active = false;
46
47 continue;
49 }
50 }
51
52 if let Some(lf) = timer.lifetime {
53 if timer.birthtime.elapsed() > lf {
54 AnimationThread::timer_stop(id as u32, timer.hwnd);
55 timer.active = false;
56 }
57 }
58 }
59
60 drop(state);
61 thread::sleep(sleep_time);
62 }
63 });
64
65 state
66 };
67}
68
69#[derive(Copy, Clone)]
70struct InnerTimer {
71 interval: Duration,
72 last_tick: Instant,
73 lifetime: Option<Duration>,
74 birthtime: Instant,
75 max_tick: Option<u64>,
76 current_tick: u64,
77 active: bool,
78 hwnd: usize,
79}
80
81struct AnimationThread {
82 timers: Vec<Option<InnerTimer>>,
83}
84
85impl AnimationThread {
86
87 fn add_timer(inner: InnerTimer) -> u32 {
88 let mut state = THREAD_STATE.lock().unwrap();
89
90 let empty = state.timers
91 .iter_mut()
92 .enumerate()
93 .find(|(_i, t)| t.is_none());
94
95 match empty {
96 Some((i, t)) => {
97 *t = Some(inner);
98 i as u32
99 },
100 None => {
101 state.timers.push(Some(inner));
102 (state.timers.len() - 1) as u32
103 }
104 }
105 }
106
107 fn reset_timer(id: u32) {
108 let mut state = THREAD_STATE.lock().unwrap();
109 if let Some(Some(t)) = state.timers.get_mut(id as usize) {
110 t.active = true;
111 t.birthtime = Instant::now();
112 t.current_tick = 0;
113 }
114 }
115
116 fn update_timer(id: u32, interval: Option<Duration>, lifetime: Option<Option<Duration>>, max_tick: Option<Option<u64>>) {
117 let mut state = THREAD_STATE.lock().unwrap();
118 if let Some(Some(t)) = state.timers.get_mut(id as usize) {
119 if let Some(v) = interval {
120 t.interval = v;
121 }
122
123 if let Some(v) = lifetime {
124 t.lifetime = v;
125 }
126
127 if let Some(v) = max_tick {
128 t.max_tick = v;
129 }
130 }
131 }
132
133 fn stop_timer(id: u32) {
134 let mut state = THREAD_STATE.lock().unwrap();
135 if let Some(Some(t)) = state.timers.get_mut(id as usize) {
136 t.active = false;
137 }
138 }
139
140 fn remove_timer(id: u32) {
141 let mut state = THREAD_STATE.lock().unwrap();
142 if let Some(t) = state.timers.get_mut(id as usize) {
143 *t = None;
144 }
145 }
146
147 pub fn timer_tick(id: u32, hwnd: usize) {
148 unsafe {
149 SendNotifyMessageW(hwnd as HWND, wh::NWG_TIMER_TICK, id as WPARAM, hwnd as LPARAM);
150 }
151 }
152
153 pub fn timer_stop(id: u32, hwnd: usize) {
154 unsafe {
155 SendNotifyMessageW(hwnd as HWND, wh::NWG_TIMER_STOP, id as WPARAM, hwnd as LPARAM);
156 }
157 }
158
159}
160
161
162#[derive(Default, PartialEq, Eq)]
199pub struct AnimationTimer {
200 pub handle: ControlHandle,
201}
202
203impl AnimationTimer {
204
205 pub fn builder() -> AnimationTimerBuilder {
206 AnimationTimerBuilder {
207 parent: None,
208 interval: Duration::from_millis(1000/60),
209 max_tick: None,
210 lifetime: None,
211 active: false,
212 }
213 }
214
215 pub fn valid(&self) -> bool {
218 if self.handle.blank() { return false; }
219 let (hwnd, _) = self.handle.timer().expect(BAD_HANDLE);
220 wh::window_valid(hwnd)
221 }
222
223 pub fn start(&self) {
228 if self.handle.blank() { panic!("{}", NOT_BOUND); }
229 let (_, id) = self.handle.timer().expect(BAD_HANDLE);
230 AnimationThread::reset_timer(id);
231 }
232
233 pub fn stop(&self) {
237 if self.handle.blank() { panic!("{}", NOT_BOUND); }
238 let (_, id) = self.handle.timer().expect(BAD_HANDLE);
239 AnimationThread::stop_timer(id);
240 }
241
242 pub fn set_interval(&self, i: Duration) {
244 if self.handle.blank() { panic!("{}", NOT_BOUND); }
245 let (_, id) = self.handle.timer().expect(BAD_HANDLE);
246 AnimationThread::update_timer(id, Some(i), None, None);
247 }
248
249 pub fn set_lifetime(&self, life: Option<Duration>) {
251 if self.handle.blank() { panic!("{}", NOT_BOUND); }
252 let (_, id) = self.handle.timer().expect(BAD_HANDLE);
253 AnimationThread::update_timer(id, None, Some(life), None);
254 }
255
256 pub fn set_max_tick(&self, max_tick: Option<u64>) {
258 if self.handle.blank() { panic!("{}", NOT_BOUND); }
259 let (_, id) = self.handle.timer().expect(BAD_HANDLE);
260 AnimationThread::update_timer(id, None, None, Some(max_tick));
261 }
262
263}
264
265impl Drop for AnimationTimer {
266
267 fn drop(&mut self) {
268 match &self.handle {
269 ControlHandle::Timer(_, id) => {
270 AnimationThread::remove_timer(*id);
271 },
272 _ => {}
273 }
274 }
275
276}
277
278pub struct AnimationTimerBuilder {
279 parent: Option<ControlHandle>,
280 interval: Duration,
281 max_tick: Option<u64>,
282 lifetime: Option<Duration>,
283 active: bool
284}
285
286impl AnimationTimerBuilder {
287
288 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> AnimationTimerBuilder {
289 self.parent = Some(p.into());
290 self
291 }
292
293 pub fn interval(mut self, interval: Duration) -> AnimationTimerBuilder {
294 self.interval = interval;
295 self
296 }
297
298 pub fn max_tick(mut self, max_tick: Option<u64>) -> AnimationTimerBuilder {
299 self.max_tick = max_tick;
300 self
301 }
302
303 pub fn lifetime(mut self, lifetime: Option<Duration>) -> AnimationTimerBuilder {
304 self.lifetime = lifetime;
305 self
306 }
307
308 pub fn active(mut self, active: bool) -> AnimationTimerBuilder {
309 self.active = active;
310 self
311 }
312
313 pub fn build(self, out: &mut AnimationTimer) -> Result<(), NwgError> {
314 let parent = match self.parent {
315 Some(p) => match p.hwnd() {
316 Some(handle) => Ok(handle),
317 None => Err(NwgError::control_create("Wrong parent type"))
318 },
319 None => Err(NwgError::no_parent("Timer"))
320 }?;
321
322 if self.interval < Duration::from_millis(1) {
323 return Err(NwgError::control_create("Timer interval cannot be smaller than 1 ms"));
324 }
325
326 let inner = InnerTimer {
327 interval: self.interval,
328 last_tick: Instant::now(),
329 lifetime: self.lifetime,
330 birthtime: Instant::now(),
331 max_tick: self.max_tick,
332 current_tick: 0,
333 active: self.active,
334 hwnd: parent as usize,
335 };
336
337 let id = AnimationThread::add_timer(inner);
338
339 *out = AnimationTimer {
340 handle: ControlHandle::Timer(parent, id)
341 };
342
343 Ok(())
344 }
345
346}