i_slint_core/
timers.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4// cSpell: ignore singleshot
5
6/*!
7    Support for timers.
8
9    Timers are just a bunch of callbacks sorted by expiry date.
10*/
11
12#![warn(missing_docs)]
13use alloc::boxed::Box;
14use alloc::vec::Vec;
15use core::{
16    cell::{Cell, RefCell},
17    num::NonZeroUsize,
18};
19
20use crate::animations::Instant;
21
22type TimerCallback = Box<dyn FnMut()>;
23type SingleShotTimerCallback = Box<dyn FnOnce()>;
24
25/// The TimerMode specifies what should happen after the timer fired.
26///
27/// Used by the [`Timer::start()`] function.
28#[derive(Copy, Clone)]
29#[repr(u8)]
30#[non_exhaustive]
31pub enum TimerMode {
32    /// A SingleShot timer is fired only once.
33    SingleShot,
34    /// A Repeated timer is fired repeatedly until it is stopped or dropped.
35    Repeated,
36}
37
38/// Timer is a handle to the timer system that triggers a callback after a specified
39/// period of time.
40///
41/// Use [`Timer::start()`] to create a timer that repeatedly triggers a callback, or
42/// [`Timer::single_shot`] to trigger a callback only once.
43///
44/// The timer will automatically stop when dropped. You must keep the Timer object
45/// around for as long as you want the timer to keep firing.
46///
47/// Timers can only be used in the thread that runs the Slint event loop. They don't
48/// fire if used in another thread.
49///
50/// ## Example
51/// ```rust,no_run
52/// # i_slint_backend_testing::init_no_event_loop();
53/// use slint::{Timer, TimerMode};
54/// let timer = Timer::default();
55/// timer.start(TimerMode::Repeated, std::time::Duration::from_millis(200), move || {
56///    println!("This will be printed every 200ms.");
57/// });
58/// // ... more initialization ...
59/// slint::run_event_loop();
60/// ```
61#[derive(Default)]
62pub struct Timer {
63    id: Cell<Option<NonZeroUsize>>,
64    /// The timer cannot be moved between treads
65    _phantom: core::marker::PhantomData<*mut ()>,
66}
67
68impl Timer {
69    /// Starts the timer with the given mode and interval, in order for the callback to called when the
70    /// timer fires. If the timer has been started previously and not fired yet, then it will be restarted.
71    ///
72    /// Arguments:
73    /// * `mode`: The timer mode to apply, i.e. whether to repeatedly fire the timer or just once.
74    /// * `interval`: The duration from now until when the timer should fire the first time, and subsequently
75    ///    for repeated [`Repeated`](TimerMode::Repeated) timers.
76    /// * `callback`: The function to call when the time has been reached or exceeded.
77    pub fn start(
78        &self,
79        mode: TimerMode,
80        interval: core::time::Duration,
81        callback: impl FnMut() + 'static,
82    ) {
83        let _ = CURRENT_TIMERS.try_with(|timers| {
84            let mut timers = timers.borrow_mut();
85            let id = timers.start_or_restart_timer(
86                self.id(),
87                mode,
88                interval,
89                CallbackVariant::MultiFire(Box::new(callback)),
90            );
91            self.set_id(Some(id));
92        });
93    }
94
95    /// Starts the timer with the duration and the callback to called when the
96    /// timer fires. It is fired only once and then deleted.
97    ///
98    /// Arguments:
99    /// * `duration`: The duration from now until when the timer should fire.
100    /// * `callback`: The function to call when the time has been reached or exceeded.
101    ///
102    /// ## Example
103    /// ```rust
104    /// # i_slint_backend_testing::init_no_event_loop();
105    /// use slint::Timer;
106    /// Timer::single_shot(std::time::Duration::from_millis(200), move || {
107    ///    println!("This will be printed after 200ms.");
108    /// });
109    /// ```
110    pub fn single_shot(duration: core::time::Duration, callback: impl FnOnce() + 'static) {
111        let _ = CURRENT_TIMERS.try_with(|timers| {
112            let mut timers = timers.borrow_mut();
113            timers.start_or_restart_timer(
114                None,
115                TimerMode::SingleShot,
116                duration,
117                CallbackVariant::SingleShot(Box::new(callback)),
118            );
119        });
120    }
121
122    /// Stops the previously started timer. Does nothing if the timer has never been started.
123    pub fn stop(&self) {
124        if let Some(id) = self.id() {
125            let _ = CURRENT_TIMERS.try_with(|timers| {
126                timers.borrow_mut().deactivate_timer(id);
127            });
128        }
129    }
130
131    /// Restarts the timer. If the timer was previously started by calling [`Self::start()`]
132    /// with a duration and callback, then the time when the callback will be next invoked
133    /// is re-calculated to be in the specified duration relative to when this function is called.
134    ///
135    /// Does nothing if the timer was never started.
136    pub fn restart(&self) {
137        if let Some(id) = self.id() {
138            let _ = CURRENT_TIMERS.try_with(|timers| {
139                timers.borrow_mut().deactivate_timer(id);
140                timers.borrow_mut().activate_timer(id);
141            });
142        }
143    }
144
145    /// Returns true if the timer is running; false otherwise.
146    pub fn running(&self) -> bool {
147        self.id()
148            .and_then(|timer_id| {
149                CURRENT_TIMERS.try_with(|timers| timers.borrow().timers[timer_id].running).ok()
150            })
151            .unwrap_or(false)
152    }
153
154    /// Change the duration of timer. If the timer was is running (see [`Self::running()`]),
155    /// then the time when the callback will be next invoked is re-calculated to be in the
156    /// specified duration relative to when this function is called.
157    ///
158    /// Arguments:
159    /// * `interval`: The duration from now until when the timer should fire. And the period of that timer
160    ///    for [`Repeated`](TimerMode::Repeated) timers.
161    pub fn set_interval(&self, interval: core::time::Duration) {
162        if let Some(id) = self.id() {
163            let _ = CURRENT_TIMERS.try_with(|timers| {
164                timers.borrow_mut().set_interval(id, interval);
165            });
166        }
167    }
168
169    /// Returns the interval of the timer. If the timer was never started, the returned duration is 0ms.
170    pub fn interval(&self) -> core::time::Duration {
171        self.id()
172            .and_then(|timer_id| {
173                CURRENT_TIMERS.try_with(|timers| timers.borrow().timers[timer_id].duration).ok()
174            })
175            .unwrap_or_default()
176    }
177
178    fn id(&self) -> Option<usize> {
179        self.id.get().map(|v| usize::from(v) - 1)
180    }
181
182    fn set_id(&self, id: Option<usize>) {
183        self.id.set(id.and_then(|v| NonZeroUsize::new(v + 1)));
184    }
185}
186
187impl Drop for Timer {
188    fn drop(&mut self) {
189        if let Some(id) = self.id() {
190            let _ = CURRENT_TIMERS.try_with(|timers| {
191                #[cfg(target_os = "android")]
192                if timers.borrow().timers.is_empty() {
193                    // There seems to be a bug in android thread_local where try_with recreates the already thread local.
194                    // But we are called from the drop of another thread local, just ignore the drop then
195                    return;
196                }
197                let callback = timers.borrow_mut().remove_timer(id);
198                // drop the callback without having CURRENT_TIMERS borrowed
199                drop(callback);
200            });
201        }
202    }
203}
204
205enum CallbackVariant {
206    Empty,
207    MultiFire(TimerCallback),
208    SingleShot(SingleShotTimerCallback),
209}
210
211struct TimerData {
212    duration: core::time::Duration,
213    mode: TimerMode,
214    running: bool,
215    /// Set to true when it is removed when the callback is still running
216    removed: bool,
217    /// true if it is in the cached the active_timers list in the maybe_activate_timers stack
218    being_activated: bool,
219
220    callback: CallbackVariant,
221}
222
223#[derive(Clone, Copy)]
224struct ActiveTimer {
225    id: usize,
226    timeout: Instant,
227}
228
229/// TimerList provides the interface to the event loop for activating times and
230/// determining the nearest timeout.
231#[derive(Default)]
232pub struct TimerList {
233    timers: slab::Slab<TimerData>,
234    active_timers: Vec<ActiveTimer>,
235    /// If a callback is currently running, this is the id of the currently running callback
236    callback_active: Option<usize>,
237}
238
239impl TimerList {
240    /// Returns the timeout of the timer that should fire the soonest, or None if there
241    /// is no timer active.
242    pub fn next_timeout() -> Option<Instant> {
243        CURRENT_TIMERS.with(|timers| {
244            timers
245                .borrow()
246                .active_timers
247                .first()
248                .map(|first_active_timer| first_active_timer.timeout)
249        })
250    }
251
252    /// Activates any expired timers by calling their callback function. Returns true if any timers were
253    /// activated; false otherwise.
254    pub fn maybe_activate_timers(now: Instant) -> bool {
255        // Shortcut: Is there any timer worth activating?
256        if TimerList::next_timeout().map(|timeout| now < timeout).unwrap_or(false) {
257            return false;
258        }
259
260        CURRENT_TIMERS.with(|timers| {
261            assert!(timers.borrow().callback_active.is_none(), "Recursion in timer code");
262
263            // Re-register all timers that expired but are repeating, as well as all that haven't expired yet. This is
264            // done in one shot to ensure a consistent state by the time the callbacks are invoked.
265            let expired_timers = {
266                let mut timers = timers.borrow_mut();
267
268                // Empty active_timers and rebuild it, to preserve insertion order across expired and not expired timers.
269                let mut active_timers = core::mem::take(&mut timers.active_timers);
270
271                let expired_vs_remaining_timers_partition_point =
272                    active_timers.partition_point(|active_timer| active_timer.timeout <= now);
273
274                let (expired_timers, timers_not_activated_this_time) =
275                    active_timers.split_at(expired_vs_remaining_timers_partition_point);
276
277                for expired_timer in expired_timers {
278                    let timer = &mut timers.timers[expired_timer.id];
279                    assert!(!timer.being_activated);
280                    timer.being_activated = true;
281
282                    if matches!(timers.timers[expired_timer.id].mode, TimerMode::Repeated) {
283                        timers.activate_timer(expired_timer.id);
284                    } else {
285                        timers.timers[expired_timer.id].running = false;
286                    }
287                }
288
289                for future_timer in timers_not_activated_this_time.iter() {
290                    timers.register_active_timer(*future_timer);
291                }
292
293                // turn `expired_timers` slice into a truncated vec.
294                active_timers.truncate(expired_vs_remaining_timers_partition_point);
295                active_timers
296            };
297
298            let any_activated = !expired_timers.is_empty();
299
300            for active_timer in expired_timers.into_iter() {
301                let mut callback = {
302                    let mut timers = timers.borrow_mut();
303
304                    timers.callback_active = Some(active_timer.id);
305
306                    // have to release the borrow on `timers` before invoking the callback,
307                    // so here we temporarily move the callback out of its permanent place
308                    core::mem::replace(
309                        &mut timers.timers[active_timer.id].callback,
310                        CallbackVariant::Empty,
311                    )
312                };
313
314                match callback {
315                    CallbackVariant::Empty => (),
316                    CallbackVariant::MultiFire(ref mut cb) => cb(),
317                    CallbackVariant::SingleShot(cb) => {
318                        cb();
319                        timers.borrow_mut().callback_active = None;
320                        timers.borrow_mut().timers.remove(active_timer.id);
321                        continue;
322                    }
323                };
324
325                let mut timers = timers.borrow_mut();
326
327                let callback_register = &mut timers.timers[active_timer.id].callback;
328
329                // only emplace back the callback if its permanent store is still Empty:
330                // if not, it means the invoked callback has restarted its own timer with a new callback
331                if matches!(callback_register, CallbackVariant::Empty) {
332                    *callback_register = callback;
333                }
334
335                timers.callback_active = None;
336                let t = &mut timers.timers[active_timer.id];
337                if t.removed {
338                    timers.timers.remove(active_timer.id);
339                } else {
340                    t.being_activated = false;
341                }
342            }
343            any_activated
344        })
345    }
346
347    fn start_or_restart_timer(
348        &mut self,
349        id: Option<usize>,
350        mode: TimerMode,
351        duration: core::time::Duration,
352        callback: CallbackVariant,
353    ) -> usize {
354        let mut timer_data = TimerData {
355            duration,
356            mode,
357            running: false,
358            removed: false,
359            callback,
360            being_activated: false,
361        };
362        let inactive_timer_id = if let Some(id) = id {
363            self.deactivate_timer(id);
364            timer_data.being_activated = self.timers[id].being_activated;
365            self.timers[id] = timer_data;
366            id
367        } else {
368            self.timers.insert(timer_data)
369        };
370        self.activate_timer(inactive_timer_id);
371        inactive_timer_id
372    }
373
374    fn deactivate_timer(&mut self, id: usize) {
375        let mut i = 0;
376        while i < self.active_timers.len() {
377            if self.active_timers[i].id == id {
378                self.active_timers.remove(i);
379                self.timers[id].running = false;
380                debug_assert!(!self.active_timers.iter().any(|t| t.id == id));
381                break;
382            } else {
383                i += 1;
384            }
385        }
386    }
387
388    fn activate_timer(&mut self, id: usize) {
389        self.register_active_timer(ActiveTimer {
390            id,
391            timeout: Instant::now() + self.timers[id].duration,
392        });
393    }
394
395    fn register_active_timer(&mut self, new_active_timer: ActiveTimer) {
396        debug_assert!(!self.active_timers.iter().any(|t| t.id == new_active_timer.id));
397        let insertion_index = self
398            .active_timers
399            .partition_point(|existing_timer| existing_timer.timeout < new_active_timer.timeout);
400        self.active_timers.insert(insertion_index, new_active_timer);
401        self.timers[new_active_timer.id].running = true;
402    }
403
404    fn remove_timer(&mut self, id: usize) -> CallbackVariant {
405        self.deactivate_timer(id);
406        let t = &mut self.timers[id];
407        if t.being_activated {
408            t.removed = true;
409            CallbackVariant::Empty
410        } else {
411            self.timers.remove(id).callback
412        }
413    }
414
415    fn set_interval(&mut self, id: usize, duration: core::time::Duration) {
416        let timer = &self.timers[id];
417        if timer.running {
418            self.deactivate_timer(id);
419            self.timers[id].duration = duration;
420            self.activate_timer(id);
421        } else {
422            self.timers[id].duration = duration;
423        }
424    }
425}
426
427crate::thread_local!(static CURRENT_TIMERS : RefCell<TimerList> = RefCell::default());
428
429#[cfg(feature = "ffi")]
430pub(crate) mod ffi {
431    #![allow(unsafe_code)]
432
433    use super::*;
434    #[allow(non_camel_case_types)]
435    type c_void = ();
436
437    struct WrapFn {
438        callback: extern "C" fn(*mut c_void),
439        user_data: *mut c_void,
440        drop_user_data: Option<extern "C" fn(*mut c_void)>,
441    }
442
443    impl Drop for WrapFn {
444        fn drop(&mut self) {
445            if let Some(x) = self.drop_user_data {
446                x(self.user_data)
447            }
448        }
449    }
450
451    impl WrapFn {
452        fn call(&self) {
453            (self.callback)(self.user_data)
454        }
455    }
456
457    /// Start a timer with the given mode, duration in millisecond and callback. A timer id may be provided (first argument).
458    /// A value of -1 for the timer id means a new timer is to be allocated.
459    /// The (new) timer id is returned.
460    /// The timer MUST be destroyed with slint_timer_destroy.
461    #[unsafe(no_mangle)]
462    pub extern "C" fn slint_timer_start(
463        id: usize,
464        mode: TimerMode,
465        duration: u64,
466        callback: extern "C" fn(*mut c_void),
467        user_data: *mut c_void,
468        drop_user_data: Option<extern "C" fn(*mut c_void)>,
469    ) -> usize {
470        let wrap = WrapFn { callback, user_data, drop_user_data };
471        let timer = Timer::default();
472        if id != 0 {
473            timer.id.set(NonZeroUsize::new(id));
474        }
475        if duration > i64::MAX as u64 {
476            // negative duration? stop the timer
477            timer.stop();
478        } else {
479            timer.start(mode, core::time::Duration::from_millis(duration), move || wrap.call());
480        }
481        timer.id.take().map(|x| usize::from(x)).unwrap_or(0)
482    }
483
484    /// Execute a callback with a delay in millisecond
485    #[unsafe(no_mangle)]
486    pub extern "C" fn slint_timer_singleshot(
487        delay: u64,
488        callback: extern "C" fn(*mut c_void),
489        user_data: *mut c_void,
490        drop_user_data: Option<extern "C" fn(*mut c_void)>,
491    ) {
492        let wrap = WrapFn { callback, user_data, drop_user_data };
493        Timer::single_shot(core::time::Duration::from_millis(delay), move || wrap.call());
494    }
495
496    /// Stop a timer and free its raw data
497    #[unsafe(no_mangle)]
498    pub extern "C" fn slint_timer_destroy(id: usize) {
499        if id == 0 {
500            return;
501        }
502        let timer = Timer { id: Cell::new(NonZeroUsize::new(id)), _phantom: Default::default() };
503        drop(timer);
504    }
505
506    /// Stop a timer
507    #[unsafe(no_mangle)]
508    pub extern "C" fn slint_timer_stop(id: usize) {
509        if id == 0 {
510            return;
511        }
512        let timer = Timer { id: Cell::new(NonZeroUsize::new(id)), _phantom: Default::default() };
513        timer.stop();
514        timer.id.take(); // Make sure that dropping the Timer doesn't unregister it. C++ will call destroy() in the destructor.
515    }
516
517    /// Restart a repeated timer
518    #[unsafe(no_mangle)]
519    pub extern "C" fn slint_timer_restart(id: usize) {
520        if id == 0 {
521            return;
522        }
523        let timer = Timer { id: Cell::new(NonZeroUsize::new(id)), _phantom: Default::default() };
524        timer.restart();
525        timer.id.take(); // Make sure that dropping the Timer doesn't unregister it. C++ will call destroy() in the destructor.
526    }
527
528    /// Returns true if the timer is running; false otherwise.
529    #[unsafe(no_mangle)]
530    pub extern "C" fn slint_timer_running(id: usize) -> bool {
531        if id == 0 {
532            return false;
533        }
534        let timer = Timer { id: Cell::new(NonZeroUsize::new(id)), _phantom: Default::default() };
535        let running = timer.running();
536        timer.id.take(); // Make sure that dropping the Timer doesn't unregister it. C++ will call destroy() in the destructor.
537        running
538    }
539
540    /// Returns the interval in milliseconds. 0 when the timer was never started.
541    #[unsafe(no_mangle)]
542    pub extern "C" fn slint_timer_interval(id: usize) -> u64 {
543        if id == 0 {
544            return 0;
545        }
546        let timer = Timer { id: Cell::new(NonZeroUsize::new(id)), _phantom: Default::default() };
547        let val = timer.interval().as_millis() as u64;
548        timer.id.take(); // Make sure that dropping the Timer doesn't unregister it. C++ will call destroy() in the destructor.
549        val
550    }
551}
552
553/**
554```rust
555i_slint_backend_testing::init_no_event_loop();
556use slint::{Timer, TimerMode};
557use std::{rc::Rc, cell::RefCell, time::Duration};
558#[derive(Default)]
559struct SharedState {
560    timer_200: Timer,
561    timer_200_called: usize,
562    timer_500: Timer,
563    timer_500_called: usize,
564    timer_once: Timer,
565    timer_once_called: usize,
566}
567let state = Rc::new(RefCell::new(SharedState::default()));
568// Note: state will be leaked because of circular dependencies: don't do that in production
569let state_ = state.clone();
570state.borrow_mut().timer_200.start(TimerMode::Repeated, Duration::from_millis(200), move || {
571    state_.borrow_mut().timer_200_called += 1;
572});
573let state_ = state.clone();
574state.borrow_mut().timer_once.start(TimerMode::Repeated, Duration::from_millis(300), move || {
575    state_.borrow_mut().timer_once_called += 1;
576    state_.borrow().timer_once.stop();
577});
578let state_ = state.clone();
579state.borrow_mut().timer_500.start(TimerMode::Repeated, Duration::from_millis(500), move || {
580    state_.borrow_mut().timer_500_called += 1;
581});
582slint::platform::update_timers_and_animations();
583i_slint_core::tests::slint_mock_elapsed_time(100);
584assert_eq!(state.borrow().timer_200_called, 0);
585assert_eq!(state.borrow().timer_once_called, 0);
586assert_eq!(state.borrow().timer_500_called, 0);
587i_slint_core::tests::slint_mock_elapsed_time(100);
588assert_eq!(state.borrow().timer_200_called, 1);
589assert_eq!(state.borrow().timer_once_called, 0);
590assert_eq!(state.borrow().timer_500_called, 0);
591i_slint_core::tests::slint_mock_elapsed_time(100);
592assert_eq!(state.borrow().timer_200_called, 1);
593assert_eq!(state.borrow().timer_once_called, 1);
594assert_eq!(state.borrow().timer_500_called, 0);
595i_slint_core::tests::slint_mock_elapsed_time(200); // total: 500
596assert_eq!(state.borrow().timer_200_called, 2);
597assert_eq!(state.borrow().timer_once_called, 1);
598assert_eq!(state.borrow().timer_500_called, 1);
599for _ in 0..10 {
600    i_slint_core::tests::slint_mock_elapsed_time(100);
601}
602// total: 1500
603assert_eq!(state.borrow().timer_200_called, 7);
604assert_eq!(state.borrow().timer_once_called, 1);
605assert_eq!(state.borrow().timer_500_called, 3);
606state.borrow().timer_once.restart();
607state.borrow().timer_200.restart();
608state.borrow().timer_500.stop();
609slint::platform::update_timers_and_animations();
610i_slint_core::tests::slint_mock_elapsed_time(100);
611assert_eq!(state.borrow().timer_200_called, 7);
612assert_eq!(state.borrow().timer_once_called, 1);
613assert_eq!(state.borrow().timer_500_called, 3);
614slint::platform::update_timers_and_animations();
615i_slint_core::tests::slint_mock_elapsed_time(100);
616assert_eq!(state.borrow().timer_200_called, 8);
617assert_eq!(state.borrow().timer_once_called, 1);
618assert_eq!(state.borrow().timer_500_called, 3);
619slint::platform::update_timers_and_animations();
620i_slint_core::tests::slint_mock_elapsed_time(100);
621assert_eq!(state.borrow().timer_200_called, 8);
622assert_eq!(state.borrow().timer_once_called, 2);
623assert_eq!(state.borrow().timer_500_called, 3);
624slint::platform::update_timers_and_animations();
625i_slint_core::tests::slint_mock_elapsed_time(1000);
626slint::platform::update_timers_and_animations();
627slint::platform::update_timers_and_animations();
628// Despite 1000ms have passed, the 200 timer is only called once because we didn't call update_timers_and_animations in between
629assert_eq!(state.borrow().timer_200_called, 9);
630assert_eq!(state.borrow().timer_once_called, 2);
631assert_eq!(state.borrow().timer_500_called, 3);
632let state_ = state.clone();
633state.borrow().timer_200.start(TimerMode::SingleShot, Duration::from_millis(200), move || {
634    state_.borrow_mut().timer_200_called += 1;
635});
636for _ in 0..5 {
637    i_slint_core::tests::slint_mock_elapsed_time(75);
638}
639assert_eq!(state.borrow().timer_200_called, 10);
640assert_eq!(state.borrow().timer_once_called, 2);
641assert_eq!(state.borrow().timer_500_called, 3);
642state.borrow().timer_200.restart();
643for _ in 0..5 {
644    i_slint_core::tests::slint_mock_elapsed_time(75);
645}
646assert_eq!(state.borrow().timer_200_called, 11);
647assert_eq!(state.borrow().timer_once_called, 2);
648assert_eq!(state.borrow().timer_500_called, 3);
649
650// Test re-starting from a callback
651let state_ = state.clone();
652state.borrow_mut().timer_500.start(TimerMode::Repeated, Duration::from_millis(500), move || {
653    state_.borrow_mut().timer_500_called += 1;
654    let state__ = state_.clone();
655    state_.borrow_mut().timer_500.start(TimerMode::Repeated, Duration::from_millis(500), move || {
656        state__.borrow_mut().timer_500_called += 1000;
657    });
658    let state__ = state_.clone();
659    state_.borrow_mut().timer_200.start(TimerMode::Repeated, Duration::from_millis(200), move || {
660        state__.borrow_mut().timer_200_called += 1000;
661    });
662});
663for _ in 0..20 {
664    i_slint_core::tests::slint_mock_elapsed_time(100);
665}
666assert_eq!(state.borrow().timer_200_called, 7011);
667assert_eq!(state.borrow().timer_once_called, 2);
668assert_eq!(state.borrow().timer_500_called, 3004);
669
670// Test set interval
671let state_ = state.clone();
672state.borrow_mut().timer_200.start(TimerMode::Repeated, Duration::from_millis(200), move || {
673    state_.borrow_mut().timer_200_called += 1;
674});
675let state_ = state.clone();
676state.borrow_mut().timer_once.start(TimerMode::Repeated, Duration::from_millis(300), move || {
677    state_.borrow_mut().timer_once_called += 1;
678    state_.borrow().timer_once.stop();
679});
680let state_ = state.clone();
681state.borrow_mut().timer_500.start(TimerMode::Repeated, Duration::from_millis(500), move || {
682    state_.borrow_mut().timer_500_called += 1;
683});
684
685let state_ = state.clone();
686slint::platform::update_timers_and_animations();
687for _ in 0..5 {
688    i_slint_core::tests::slint_mock_elapsed_time(100);
689}
690slint::platform::update_timers_and_animations();
691assert_eq!(state.borrow().timer_200_called, 7013);
692assert_eq!(state.borrow().timer_once_called, 3);
693assert_eq!(state.borrow().timer_500_called, 3005);
694
695for _ in 0..20 {
696    state.borrow().timer_200.set_interval(Duration::from_millis(200 * 2));
697    state.borrow().timer_once.set_interval(Duration::from_millis(300 * 2));
698    state.borrow().timer_500.set_interval(Duration::from_millis(500 * 2));
699
700    assert_eq!(state.borrow().timer_200_called, 7013);
701    assert_eq!(state.borrow().timer_once_called, 3);
702    assert_eq!(state.borrow().timer_500_called, 3005);
703
704    i_slint_core::tests::slint_mock_elapsed_time(100);
705}
706
707slint::platform::update_timers_and_animations();
708for _ in 0..9 {
709    i_slint_core::tests::slint_mock_elapsed_time(100);
710}
711slint::platform::update_timers_and_animations();
712assert_eq!(state.borrow().timer_200_called, 7015);
713assert_eq!(state.borrow().timer_once_called, 3);
714assert_eq!(state.borrow().timer_500_called, 3006);
715
716state.borrow().timer_200.stop();
717state.borrow().timer_500.stop();
718
719state.borrow_mut().timer_once.restart();
720for _ in 0..4 {
721    i_slint_core::tests::slint_mock_elapsed_time(100);
722}
723assert_eq!(state.borrow().timer_once_called, 3);
724for _ in 0..4 {
725    i_slint_core::tests::slint_mock_elapsed_time(100);
726}
727assert_eq!(state.borrow().timer_once_called, 4);
728
729state.borrow_mut().timer_once.stop();
730i_slint_core::tests::slint_mock_elapsed_time(1000);
731
732assert_eq!(state.borrow().timer_200_called, 7015);
733assert_eq!(state.borrow().timer_once_called, 4);
734assert_eq!(state.borrow().timer_500_called, 3006);
735```
736 */
737#[cfg(doctest)]
738const _TIMER_TESTS: () = ();
739
740/**
741 * Test that deleting an active timer from a timer event works.
742```rust
743// There is a 200 ms timer that increase variable1
744// after 500ms, that timer is destroyed by a single shot timer,
745// and a new new timer  increase variable2
746i_slint_backend_testing::init_no_event_loop();
747use slint::{Timer, TimerMode};
748use std::{rc::Rc, cell::RefCell, time::Duration};
749#[derive(Default)]
750struct SharedState {
751    repeated_timer: Timer,
752    variable1: usize,
753    variable2: usize,
754}
755let state = Rc::new(RefCell::new(SharedState::default()));
756// Note: state will be leaked because of circular dependencies: don't do that in production
757let state_ = state.clone();
758state.borrow_mut().repeated_timer.start(TimerMode::Repeated, Duration::from_millis(200), move || {
759    state_.borrow_mut().variable1 += 1;
760});
761let state_ = state.clone();
762Timer::single_shot(Duration::from_millis(500), move || {
763    state_.borrow_mut().repeated_timer = Default::default();
764    let state = state_.clone();
765    state_.borrow_mut().repeated_timer.start(TimerMode::Repeated, Duration::from_millis(200), move || {
766        state.borrow_mut().variable2 += 1;
767    })
768} );
769i_slint_core::tests::slint_mock_elapsed_time(10);
770assert_eq!(state.borrow().variable1, 0);
771assert_eq!(state.borrow().variable2, 0);
772i_slint_core::tests::slint_mock_elapsed_time(200);
773assert_eq!(state.borrow().variable1, 1);
774assert_eq!(state.borrow().variable2, 0);
775i_slint_core::tests::slint_mock_elapsed_time(200);
776assert_eq!(state.borrow().variable1, 2);
777assert_eq!(state.borrow().variable2, 0);
778i_slint_core::tests::slint_mock_elapsed_time(100);
779// More than 500ms have elapsed, the single shot timer should have been activated, but that has no effect on variable 1 and 2
780// This should just restart the timer so that the next change should happen 200ms from now
781assert_eq!(state.borrow().variable1, 2);
782assert_eq!(state.borrow().variable2, 0);
783i_slint_core::tests::slint_mock_elapsed_time(110);
784assert_eq!(state.borrow().variable1, 2);
785assert_eq!(state.borrow().variable2, 0);
786i_slint_core::tests::slint_mock_elapsed_time(100);
787assert_eq!(state.borrow().variable1, 2);
788assert_eq!(state.borrow().variable2, 1);
789i_slint_core::tests::slint_mock_elapsed_time(100);
790assert_eq!(state.borrow().variable1, 2);
791assert_eq!(state.borrow().variable2, 1);
792i_slint_core::tests::slint_mock_elapsed_time(100);
793assert_eq!(state.borrow().variable1, 2);
794assert_eq!(state.borrow().variable2, 2);
795```
796 */
797#[cfg(doctest)]
798const _BUG3019: () = ();
799
800/**
801 * Test that starting a singleshot timer works
802```rust
803// There is a 200 ms singleshot timer that increase variable1
804i_slint_backend_testing::init_no_event_loop();
805use slint::{Timer, TimerMode};
806use std::{rc::Rc, cell::RefCell, time::Duration};
807#[derive(Default)]
808struct SharedState {
809    variable1: usize,
810}
811let state = Rc::new(RefCell::new(SharedState::default()));
812let state_ = state.clone();
813let timer = Timer::default();
814
815timer.start(TimerMode::SingleShot, Duration::from_millis(200), move || {
816    state_.borrow_mut().variable1 += 1;
817});
818
819// Singleshot timer set up and run...
820assert!(timer.running());
821i_slint_core::tests::slint_mock_elapsed_time(10);
822assert!(timer.running());
823assert_eq!(state.borrow().variable1, 0);
824i_slint_core::tests::slint_mock_elapsed_time(200);
825assert_eq!(state.borrow().variable1, 1);
826assert!(!timer.running());
827i_slint_core::tests::slint_mock_elapsed_time(200);
828assert_eq!(state.borrow().variable1, 1); // It's singleshot, it only triggers once!
829assert!(!timer.running());
830
831// Restart a previously set up singleshot timer
832timer.restart();
833assert!(timer.running());
834assert_eq!(state.borrow().variable1, 1);
835i_slint_core::tests::slint_mock_elapsed_time(200);
836assert_eq!(state.borrow().variable1, 2);
837assert!(!timer.running());
838i_slint_core::tests::slint_mock_elapsed_time(200);
839assert_eq!(state.borrow().variable1, 2); // It's singleshot, it only triggers once!
840assert!(!timer.running());
841
842// Stop a non-running singleshot timer
843timer.stop();
844assert!(!timer.running());
845assert_eq!(state.borrow().variable1, 2);
846i_slint_core::tests::slint_mock_elapsed_time(200);
847assert_eq!(state.borrow().variable1, 2);
848assert!(!timer.running());
849i_slint_core::tests::slint_mock_elapsed_time(200);
850assert_eq!(state.borrow().variable1, 2); // It's singleshot, it only triggers once!
851assert!(!timer.running());
852
853// Stop a running singleshot timer
854timer.restart();
855assert!(timer.running());
856assert_eq!(state.borrow().variable1, 2);
857i_slint_core::tests::slint_mock_elapsed_time(10);
858timer.stop();
859assert!(!timer.running());
860i_slint_core::tests::slint_mock_elapsed_time(200);
861assert_eq!(state.borrow().variable1, 2);
862assert!(!timer.running());
863i_slint_core::tests::slint_mock_elapsed_time(200);
864assert_eq!(state.borrow().variable1, 2); // It's singleshot, it only triggers once!
865assert!(!timer.running());
866
867// set_interval on a non-running singleshot timer
868timer.set_interval(Duration::from_millis(300));
869assert!(!timer.running());
870i_slint_core::tests::slint_mock_elapsed_time(1000);
871assert_eq!(state.borrow().variable1, 2);
872assert!(!timer.running());
873timer.restart();
874assert!(timer.running());
875i_slint_core::tests::slint_mock_elapsed_time(200);
876assert_eq!(state.borrow().variable1, 2);
877assert!(timer.running());
878i_slint_core::tests::slint_mock_elapsed_time(200);
879assert_eq!(state.borrow().variable1, 3);
880assert!(!timer.running());
881i_slint_core::tests::slint_mock_elapsed_time(300);
882assert_eq!(state.borrow().variable1, 3); // It's singleshot, it only triggers once!
883assert!(!timer.running());
884
885// set_interval on a running singleshot timer
886timer.restart();
887assert!(timer.running());
888assert_eq!(state.borrow().variable1, 3);
889i_slint_core::tests::slint_mock_elapsed_time(290);
890timer.set_interval(Duration::from_millis(400));
891assert!(timer.running());
892i_slint_core::tests::slint_mock_elapsed_time(200);
893assert_eq!(state.borrow().variable1, 3);
894assert!(timer.running());
895i_slint_core::tests::slint_mock_elapsed_time(250);
896assert_eq!(state.borrow().variable1, 4);
897assert!(!timer.running());
898i_slint_core::tests::slint_mock_elapsed_time(400);
899assert_eq!(state.borrow().variable1, 4); // It's singleshot, it only triggers once!
900assert!(!timer.running());
901```
902 */
903#[cfg(doctest)]
904const _SINGLESHOT_START: () = ();
905
906/**
907 * Test that it's possible to start a new timer from within Drop of a timer's closure.
908 * This may happen when a timer's closure is dropped, that closure holds the last reference
909 * to a component, that component is destroyed, and the accesskit code schedules a reload_tree
910 * via a single shot.
911```rust
912i_slint_backend_testing::init_no_event_loop();
913use slint::{Timer, TimerMode};
914use std::{rc::Rc, cell::Cell, time::Duration};
915#[derive(Default)]
916struct CapturedInClosure {
917    last_fired: Option<Rc<Cell<bool>>>,
918}
919impl Drop for CapturedInClosure {
920    fn drop(&mut self) {
921        if let Some(last_fired) = self.last_fired.as_ref().cloned() {
922            Timer::single_shot(Duration::from_millis(100), move || last_fired.set(true));
923        }
924    }
925}
926
927let last_fired = Rc::new(Cell::new(false));
928
929let mut cap_in_clos = CapturedInClosure::default();
930
931let timer_to_stop = Timer::default();
932timer_to_stop.start(TimerMode::Repeated, Duration::from_millis(100), {
933    let last_fired = last_fired.clone();
934    move || {
935    cap_in_clos.last_fired = Some(last_fired.clone());
936}});
937
938assert_eq!(last_fired.get(), false);
939i_slint_core::tests::slint_mock_elapsed_time(110);
940assert_eq!(last_fired.get(), false);
941drop(timer_to_stop);
942
943i_slint_core::tests::slint_mock_elapsed_time(110);
944assert_eq!(last_fired.get(), true);
945```
946 */
947#[cfg(doctest)]
948const _TIMER_CLOSURE_DROP_STARTS_NEW_TIMER: () = ();
949
950/**
951 * Test that it's possible to set a timer's interval from within the callback.
952```rust
953i_slint_backend_testing::init_no_event_loop();
954use slint::{Timer, TimerMode};
955use std::{rc::Rc, cell::RefCell, time::Duration};
956#[derive(Default)]
957struct SharedState {
958    // Note: state will be leaked because of circular dependencies: don't do that in production
959    timer: Timer,
960    variable1: usize,
961}
962let state = Rc::new(RefCell::new(SharedState::default()));
963let state_ = state.clone();
964state.borrow().timer.start(TimerMode::Repeated, Duration::from_millis(200), move || {
965    state_.borrow_mut().variable1 += 1;
966    let variable1 = state_.borrow().variable1;
967    if variable1 == 2 {
968        state_.borrow().timer.set_interval(Duration::from_millis(500));
969    } else if variable1 == 3 {
970        state_.borrow().timer.set_interval(Duration::from_millis(100));
971    }
972});
973
974assert!(state.borrow().timer.running());
975i_slint_core::tests::slint_mock_elapsed_time(10);
976assert!(state.borrow().timer.running());
977assert_eq!(state.borrow().variable1, 0);
978i_slint_core::tests::slint_mock_elapsed_time(200);
979assert_eq!(state.borrow().variable1, 1); // fired
980assert!(state.borrow().timer.running());
981i_slint_core::tests::slint_mock_elapsed_time(180);
982assert_eq!(state.borrow().variable1, 1);
983assert!(state.borrow().timer.running());
984i_slint_core::tests::slint_mock_elapsed_time(30);
985assert_eq!(state.borrow().variable1, 2); // fired
986assert!(state.borrow().timer.running());
987// now the timer interval should be 500
988i_slint_core::tests::slint_mock_elapsed_time(480);
989assert_eq!(state.borrow().variable1, 2);
990assert!(state.borrow().timer.running());
991i_slint_core::tests::slint_mock_elapsed_time(30);
992assert_eq!(state.borrow().variable1, 3); // fired
993assert!(state.borrow().timer.running());
994// now the timer interval should be 100
995i_slint_core::tests::slint_mock_elapsed_time(100);
996assert_eq!(state.borrow().variable1, 4); // fired
997assert!(state.borrow().timer.running());
998i_slint_core::tests::slint_mock_elapsed_time(100);
999assert_eq!(state.borrow().variable1, 5); // fired
1000assert!(state.borrow().timer.running());
1001```
1002 */
1003#[cfg(doctest)]
1004const _BUG6141_SET_INTERVAL_FROM_CALLBACK: () = ();
1005
1006/**
1007 * Test that a timer can't be activated twice.
1008```rust
1009i_slint_backend_testing::init_no_event_loop();
1010use slint::{Timer, TimerMode};
1011use std::{rc::Rc, cell::Cell, time::Duration};
1012
1013let later_timer_expiration_count = Rc::new(Cell::new(0));
1014
1015let sooner_timer = Timer::default();
1016let later_timer = Rc::new(Timer::default());
1017later_timer.start(TimerMode::SingleShot, Duration::from_millis(500), {
1018    let later_timer_expiration_count = later_timer_expiration_count.clone();
1019    move || {
1020        later_timer_expiration_count.set(later_timer_expiration_count.get() + 1);
1021    }
1022});
1023
1024sooner_timer.start(TimerMode::SingleShot, Duration::from_millis(100), {
1025    let later_timer = later_timer.clone();
1026    let later_timer_expiration_count = later_timer_expiration_count.clone();
1027    move || {
1028    later_timer.start(TimerMode::SingleShot, Duration::from_millis(600), {
1029        let later_timer_expiration_count = later_timer_expiration_count.clone();
1030        move || {
1031            later_timer_expiration_count.set(later_timer_expiration_count.get() + 1);
1032        }
1033    });
1034}});
1035
1036assert_eq!(later_timer_expiration_count.get(), 0);
1037i_slint_core::tests::slint_mock_elapsed_time(110);
1038assert_eq!(later_timer_expiration_count.get(), 0);
1039i_slint_core::tests::slint_mock_elapsed_time(400);
1040assert_eq!(later_timer_expiration_count.get(), 0);
1041i_slint_core::tests::slint_mock_elapsed_time(800);
1042assert_eq!(later_timer_expiration_count.get(), 1);
1043```
1044 */
1045#[cfg(doctest)]
1046const _DOUBLY_REGISTER_ACTIVE_TIMER: () = ();
1047
1048/**
1049 * Test that a timer can't be activated twice.
1050```rust
1051i_slint_backend_testing::init_no_event_loop();
1052use slint::{Timer, TimerMode};
1053use std::{rc::Rc, cell::Cell, time::Duration};
1054
1055let later_timer_expiration_count = Rc::new(Cell::new(0));
1056
1057let sooner_timer = Timer::default();
1058let later_timer = Rc::new(Timer::default());
1059later_timer.start(TimerMode::Repeated, Duration::from_millis(110), {
1060    let later_timer_expiration_count = later_timer_expiration_count.clone();
1061    move || {
1062        later_timer_expiration_count.set(later_timer_expiration_count.get() + 1);
1063    }
1064});
1065
1066sooner_timer.start(TimerMode::SingleShot, Duration::from_millis(100), {
1067    let later_timer = later_timer.clone();
1068    let later_timer_expiration_count = later_timer_expiration_count.clone();
1069    move || {
1070    later_timer.start(TimerMode::Repeated, Duration::from_millis(110), {
1071        let later_timer_expiration_count = later_timer_expiration_count.clone();
1072        move || {
1073            later_timer_expiration_count.set(later_timer_expiration_count.get() + 1);
1074        }
1075    });
1076}});
1077
1078assert_eq!(later_timer_expiration_count.get(), 0);
1079i_slint_core::tests::slint_mock_elapsed_time(120);
1080assert_eq!(later_timer_expiration_count.get(), 1);
1081```
1082 */
1083#[cfg(doctest)]
1084const _DOUBLY_REGISTER_ACTIVE_TIMER_2: () = ();
1085
1086/**
1087 * Test that a timer that's being activated can be restarted and dropped in one go.
1088```rust
1089i_slint_backend_testing::init_no_event_loop();
1090use slint::{Timer, TimerMode};
1091use std::{cell::RefCell, rc::Rc, time::Duration};
1092
1093let destructive_timer = Rc::new(RefCell::new(Some(Timer::default())));
1094
1095destructive_timer.borrow().as_ref().unwrap().start(TimerMode::Repeated, Duration::from_millis(110), {
1096    let destructive_timer = destructive_timer.clone();
1097    move || {
1098        // start() used to reset the `being_activated` flag...
1099        destructive_timer.borrow().as_ref().unwrap().start(TimerMode::Repeated, Duration::from_millis(110), || {});
1100        // ... which would make this drop remove the timer from the timer list altogether and continued processing
1101        // of the timer would panic as the id isn't valid anymore.
1102        drop(destructive_timer.take());
1103    }
1104});
1105
1106drop(destructive_timer);
1107i_slint_core::tests::slint_mock_elapsed_time(120);
1108```
1109 */
1110#[cfg(doctest)]
1111const _RESTART_TIMER_BEING_ACTIVATED: () = ();
1112
1113/**
1114 * Test that a future timer can be stopped from the activation callback of an earlier timer.
1115```rust
1116i_slint_backend_testing::init_no_event_loop();
1117use slint::{Timer, TimerMode};
1118use std::{rc::Rc, cell::Cell, time::Duration};
1119
1120let later_timer_expiration_count = Rc::new(Cell::new(0));
1121
1122let sooner_timer = Timer::default();
1123let later_timer = Rc::new(Timer::default());
1124later_timer.start(TimerMode::SingleShot, Duration::from_millis(500), {
1125    let later_timer_expiration_count = later_timer_expiration_count.clone();
1126    move || {
1127        later_timer_expiration_count.set(later_timer_expiration_count.get() + 1);
1128    }
1129});
1130
1131sooner_timer.start(TimerMode::SingleShot, Duration::from_millis(100), {
1132    let later_timer = later_timer.clone();
1133    let later_timer_expiration_count = later_timer_expiration_count.clone();
1134    move || {
1135        later_timer.stop();
1136    }
1137});
1138
1139assert_eq!(later_timer_expiration_count.get(), 0);
1140assert!(later_timer.running());
1141i_slint_core::tests::slint_mock_elapsed_time(110);
1142assert_eq!(later_timer_expiration_count.get(), 0);
1143assert!(!later_timer.running());
1144i_slint_core::tests::slint_mock_elapsed_time(800);
1145assert_eq!(later_timer_expiration_count.get(), 0);
1146assert!(!later_timer.running());
1147i_slint_core::tests::slint_mock_elapsed_time(800);
1148i_slint_core::tests::slint_mock_elapsed_time(800);
1149assert_eq!(later_timer_expiration_count.get(), 0);
1150```
1151 */
1152#[cfg(doctest)]
1153const _STOP_FUTURE_TIMER_DURING_ACTIVATION_OF_EARLIER: () = ();
1154
1155/**
1156 * Test for issue #8897
1157```rust
1158use slint::TimerMode;
1159static DROP_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1160static CALL1_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1161static CALL2_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1162std::thread::spawn(move || {
1163    struct StartTimerInDrop{};
1164    impl Drop for StartTimerInDrop {
1165        fn drop(&mut self) {
1166            DROP_COUNT.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
1167            slint::Timer::single_shot(std::time::Duration::from_millis(100), move || {
1168                println!("Timer fired");
1169            });
1170            let timer = slint::Timer::default();
1171            timer.start(TimerMode::Repeated, std::time::Duration::from_millis(100), move || {
1172                println!("fired");
1173            });
1174            timer.restart();
1175            timer.stop();
1176        }
1177    }
1178
1179    thread_local! { static START_TIMER_IN_DROP: StartTimerInDrop = StartTimerInDrop {}; }
1180    let timer = START_TIMER_IN_DROP.with(|_| { });
1181    thread_local! { static TIMER2: slint::Timer = slint::Timer::default(); }
1182    TIMER2.with(|timer| {
1183        timer.start(TimerMode::Repeated, std::time::Duration::from_millis(100), move || {
1184            CALL2_COUNT.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
1185        });
1186    });
1187
1188
1189    i_slint_backend_testing::init_no_event_loop();
1190    slint::Timer::single_shot(std::time::Duration::from_millis(100), move || {
1191            CALL1_COUNT.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
1192    });
1193    i_slint_core::tests::slint_mock_elapsed_time(50);
1194
1195    assert_eq!(CALL1_COUNT.load(std::sync::atomic::Ordering::SeqCst), 0);
1196    assert_eq!(CALL2_COUNT.load(std::sync::atomic::Ordering::SeqCst), 0);
1197    i_slint_core::tests::slint_mock_elapsed_time(60);
1198    assert_eq!(CALL1_COUNT.load(std::sync::atomic::Ordering::SeqCst), 1);
1199    assert_eq!(CALL2_COUNT.load(std::sync::atomic::Ordering::SeqCst), 1);
1200    i_slint_core::tests::slint_mock_elapsed_time(60);
1201}).join().unwrap();
1202assert_eq!(DROP_COUNT.load(std::sync::atomic::Ordering::SeqCst), 1);
1203assert_eq!(CALL1_COUNT.load(std::sync::atomic::Ordering::SeqCst), 1);
1204assert_eq!(CALL2_COUNT.load(std::sync::atomic::Ordering::SeqCst), 1);
1205```
1206 */
1207#[cfg(doctest)]
1208const _TIMER_AT_EXIT: () = ();