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