Skip to main content

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