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    /// Identifies both the list this timer belongs to and its slot in that list, packed
64    /// into one word. See [`Timer::resolve`] for the encoding.
65    id: Cell<Option<NonZeroUsize>>,
66    /// The timer cannot be moved between treads
67    _phantom: core::marker::PhantomData<*mut ()>,
68}
69
70/// `Timer` must stay exactly one pointer wide: the C++ `slint::Timer` mirrors it by hand as
71/// a single `uintptr_t` (see `api/cpp/include/private/slint_timer.h`), and native items such
72/// as `TooltipArea` and `ContextMenu` embed it in structs that C++ lays out. Widening it
73/// silently corrupts those items rather than failing to build, so pin the size here.
74const _: () = assert!(
75    core::mem::size_of::<Timer>() == core::mem::size_of::<usize>(),
76    "Timer must stay one word wide to match the hand-written C++ slint::Timer"
77);
78
79impl Timer {
80    /// Starts the timer with the given mode and interval, in order for the callback to called when the
81    /// timer fires. If the timer has been started previously, then it will be restarted, no matter if
82    /// it has already been fired or not.
83    ///
84    /// Arguments:
85    /// * `mode`: The timer mode to apply, i.e. whether to repeatedly fire the timer or just once.
86    /// * `interval`: The duration from now until when the timer should fire the first time, and subsequently
87    ///   for repeated [`Repeated`](TimerMode::Repeated) timers.
88    /// * `callback`: The function to call when the time has been reached or exceeded.
89    pub fn start(
90        &self,
91        mode: TimerMode,
92        interval: core::time::Duration,
93        callback: impl FnMut() + 'static,
94    ) {
95        // Keep firing on the list this timer already belongs to. Only a timer that was
96        // never started, or whose list went away with its context, picks a new one.
97        let (timers, slot) = match self.resolve() {
98            Some(resolved) => resolved,
99            None => {
100                let Some(timers) = current_timers() else { return };
101                (timers, None)
102            }
103        };
104
105        let handle = list_handle(&timers);
106        let slot = timers.borrow_mut().start_or_restart_timer(
107            slot,
108            mode,
109            interval,
110            CallbackVariant::MultiFire(Box::new(callback)),
111        );
112        self.set_ids(handle, Some(slot));
113    }
114
115    /// Starts the timer on `ctx` rather than on whichever context happens to be current.
116    ///
117    /// A timer that already belongs to a live context keeps firing on that one; this only
118    /// decides where a timer that has never been started registers. Used by items, which
119    /// own their `Timer` as a field but can reach their window's context when they start it.
120    pub(crate) fn start_on(
121        &self,
122        ctx: &crate::SlintContext,
123        mode: TimerMode,
124        interval: core::time::Duration,
125        callback: impl FnMut() + 'static,
126    ) {
127        if self.resolve().is_none() {
128            self.set_ids(list_handle(&ctx.0.timers), None);
129        }
130        self.start(mode, interval, callback);
131    }
132
133    /// Starts the timer with the duration and the callback to called when the
134    /// timer fires. It is fired only once and then deleted.
135    ///
136    /// Arguments:
137    /// * `duration`: The duration from now until when the timer should fire.
138    /// * `callback`: The function to call when the time has been reached or exceeded.
139    ///
140    /// ## Example
141    /// ```rust
142    /// # i_slint_backend_testing::init_no_event_loop();
143    /// use slint::Timer;
144    /// Timer::single_shot(std::time::Duration::from_millis(200), move || {
145    ///    println!("This will be printed after 200ms.");
146    /// });
147    /// ```
148    pub fn single_shot(duration: core::time::Duration, callback: impl FnOnce() + 'static) {
149        if let Some(timers) = current_timers() {
150            timers.borrow_mut().start_or_restart_timer(
151                None,
152                TimerMode::SingleShot,
153                duration,
154                CallbackVariant::SingleShot(Box::new(callback)),
155            );
156        }
157    }
158
159    /// Stops the previously started timer. Does nothing if the timer has never been started.
160    pub fn stop(&self) {
161        if let Some((timers, slot)) = self.started() {
162            timers.borrow_mut().deactivate_timer(slot);
163        }
164    }
165
166    /// Restarts the timer. If the timer was previously started by calling [`Self::start()`]
167    /// with a duration and callback, then the time when the callback will be next invoked
168    /// is re-calculated to be in the specified duration relative to when this function is called.
169    ///
170    /// Does nothing if the timer was never started.
171    pub fn restart(&self) {
172        if let Some((timers, slot)) = self.started() {
173            timers.borrow_mut().deactivate_timer(slot);
174            timers.borrow_mut().activate_timer(slot);
175        }
176    }
177
178    /// Returns true if the timer is running; false otherwise.
179    pub fn running(&self) -> bool {
180        self.started().is_some_and(|(timers, slot)| timers.borrow().timers[slot].running)
181    }
182
183    /// Change the duration of timer. If the timer was is running (see [`Self::running()`]),
184    /// then the time when the callback will be next invoked is re-calculated to be in the
185    /// specified duration relative to when this function is called.
186    ///
187    /// Arguments:
188    /// * `interval`: The duration from now until when the timer should fire. And the period of that timer
189    ///   for [`Repeated`](TimerMode::Repeated) timers.
190    pub fn set_interval(&self, interval: core::time::Duration) {
191        if let Some((timers, slot)) = self.started() {
192            timers.borrow_mut().set_interval(slot, interval);
193        }
194    }
195
196    /// Returns the interval of the timer. If the timer was never started, the returned duration is 0ms.
197    pub fn interval(&self) -> core::time::Duration {
198        self.started()
199            .map(|(timers, slot)| timers.borrow().timers[slot].duration)
200            .unwrap_or_default()
201    }
202
203    /// A timer that will register in `timers` when started, rather than in whichever list
204    /// is current at that point. Backs [`SlintContext::new_timer`](crate::SlintContext::new_timer).
205    pub(crate) fn with_list(timers: &TimerListRc) -> Self {
206        let timer = Self::default();
207        timer.set_ids(list_handle(timers), None);
208        timer
209    }
210
211    /// Decodes `id` into the list this timer belongs to and its slot in that list.
212    ///
213    /// `None` when the timer has no list at all, or when that list has gone away with the
214    /// context owning it — in which case the slot it names no longer means anything, and
215    /// every operation but [`Self::start`] does nothing. The inner `Option` is `None` for a
216    /// timer that has a list but was never started, as [`Self::with_list`] produces.
217    fn resolve(&self) -> Option<(TimerListRc, Option<usize>)> {
218        let id = usize::from(self.id.get()?);
219        let timers = list_from_handle(id >> LIST_SHIFT)?;
220        Some((timers, (id & SLOT_MASK).checked_sub(1)))
221    }
222
223    /// The list and slot of a timer that has been started and whose list is still alive.
224    /// `None` in every other case, which is what all operations but [`Self::start`] want.
225    fn started(&self) -> Option<(TimerListRc, usize)> {
226        let (timers, slot) = self.resolve()?;
227        Some((timers, slot?))
228    }
229
230    fn set_ids(&self, list: usize, slot: Option<usize>) {
231        let slot = slot.map_or(0, |slot| slot + 1);
232        debug_assert!(slot <= SLOT_MASK, "too many timers in one list");
233        debug_assert!(list <= usize::MAX >> LIST_SHIFT, "too many timer lists");
234        self.id.set(NonZeroUsize::new((list << LIST_SHIFT) | slot));
235    }
236}
237
238/// Registers a single-shot timer in `timers` rather than in whichever list is current.
239/// Backs [`SlintContext::single_shot`](crate::SlintContext::single_shot).
240pub(crate) fn single_shot_on(
241    timers: &TimerListRc,
242    duration: core::time::Duration,
243    callback: impl FnOnce() + 'static,
244) {
245    timers.borrow_mut().start_or_restart_timer(
246        None,
247        TimerMode::SingleShot,
248        duration,
249        CallbackVariant::SingleShot(Box::new(callback)),
250    );
251}
252
253impl Drop for Timer {
254    fn drop(&mut self) {
255        if let Some((timers, slot)) = self.started() {
256            #[cfg(target_os = "android")]
257            if timers.borrow().timers.is_empty() {
258                // There seems to be a bug in android thread_local where try_with recreates the already thread local.
259                // But we are called from the drop of another thread local, just ignore the drop then
260                return;
261            }
262            let callback = timers.borrow_mut().remove_timer(slot);
263            // drop the callback without having the list borrowed
264            drop(callback);
265        }
266    }
267}
268
269enum CallbackVariant {
270    Empty,
271    MultiFire(TimerCallback),
272    SingleShot(SingleShotTimerCallback),
273}
274
275struct TimerData {
276    duration: core::time::Duration,
277    mode: TimerMode,
278    running: bool,
279    /// Set to true when it is removed when the callback is still running
280    removed: bool,
281    /// true if it is in the cached the active_timers list in the maybe_activate_timers stack
282    being_activated: bool,
283
284    callback: CallbackVariant,
285}
286
287#[derive(Clone, Copy)]
288struct ActiveTimer {
289    id: usize,
290    timeout: Instant,
291}
292
293/// TimerList provides the interface to the event loop for activating times and
294/// determining the nearest timeout.
295#[derive(Default)]
296pub struct TimerList {
297    timers: slab::Slab<TimerData>,
298    active_timers: Vec<ActiveTimer>,
299    /// If a callback is currently running, this is the id of the currently running callback
300    callback_active: Option<usize>,
301    /// This list's index in [`ThreadTimers::lists`], assigned the first time a timer id naming it
302    /// is handed out. Cached here so encoding an id doesn't have to search the registry.
303    handle: Option<usize>,
304    /// The context this list belongs to, so that a deadline is computed on the same clock it
305    /// is later compared against. `None` until a context takes the list over — the origin is
306    /// the platform's start time, and without a platform there is no origin.
307    context: Option<crate::SlintContextWeak>,
308}
309
310impl TimerList {
311    /// Returns the timeout of the timer that should fire the soonest, or None if there
312    /// is no timer active.
313    pub fn next_timeout() -> Option<Instant> {
314        current_timers().and_then(|timers| timers.borrow().first_timeout())
315    }
316
317    /// Activates any expired timers by calling their callback function. Returns true if any timers were
318    /// activated; false otherwise.
319    pub fn maybe_activate_timers(now: Instant) -> bool {
320        current_timers().is_some_and(|timers| Self::activate_expired(&timers, now))
321    }
322
323    /// The current instant on this list's clock, or the zero origin while no context owns
324    /// it — the same origin the deadlines of any timer started that early were computed
325    /// against, so they stay consistent until a context adopts them.
326    fn now(&self) -> Instant {
327        self.context
328            .as_ref()
329            .and_then(|ctx| ctx.upgrade())
330            .map_or_else(Instant::default, |ctx| Instant::now(&ctx))
331    }
332
333    /// The timeout of the timer in this list that should fire the soonest, or None if
334    /// none is active.
335    pub(crate) fn first_timeout(&self) -> Option<Instant> {
336        self.active_timers.first().map(|first_active_timer| first_active_timer.timeout)
337    }
338
339    /// Activates the timers in `timers` that have expired by `now`. Returns true if any
340    /// timer was activated; false otherwise.
341    ///
342    /// Takes the list by `&RefCell` rather than `&mut self` because the borrow has to be
343    /// released around every callback: a callback may start, stop or drop timers.
344    pub(crate) fn activate_expired(timers: &RefCell<Self>, now: Instant) -> bool {
345        // Shortcut: Is there any timer worth activating?
346        if timers.borrow().first_timeout().map(|timeout| now < timeout).unwrap_or(false) {
347            return false;
348        }
349
350        assert!(timers.borrow().callback_active.is_none(), "Recursion in timer code");
351
352        // Re-register all timers that expired but are repeating, as well as all that haven't expired yet. This is
353        // done in one shot to ensure a consistent state by the time the callbacks are invoked.
354        let expired_timers = {
355            let mut timers = timers.borrow_mut();
356
357            // Empty active_timers and rebuild it, to preserve insertion order across expired and not expired timers.
358            let mut active_timers = core::mem::take(&mut timers.active_timers);
359
360            let expired_vs_remaining_timers_partition_point =
361                active_timers.partition_point(|active_timer| active_timer.timeout <= now);
362
363            let (expired_timers, timers_not_activated_this_time) =
364                active_timers.split_at(expired_vs_remaining_timers_partition_point);
365
366            for expired_timer in expired_timers {
367                let timer = &mut timers.timers[expired_timer.id];
368                assert!(!timer.being_activated);
369                timer.being_activated = true;
370
371                if matches!(timers.timers[expired_timer.id].mode, TimerMode::Repeated) {
372                    timers.activate_timer(expired_timer.id);
373                } else {
374                    timers.timers[expired_timer.id].running = false;
375                }
376            }
377
378            for future_timer in timers_not_activated_this_time.iter() {
379                timers.register_active_timer(*future_timer);
380            }
381
382            // turn `expired_timers` slice into a truncated vec.
383            active_timers.truncate(expired_vs_remaining_timers_partition_point);
384            active_timers
385        };
386
387        let any_activated = !expired_timers.is_empty();
388
389        for active_timer in expired_timers.into_iter() {
390            let mut callback = {
391                let mut timers = timers.borrow_mut();
392
393                timers.callback_active = Some(active_timer.id);
394
395                // have to release the borrow on `timers` before invoking the callback,
396                // so here we temporarily move the callback out of its permanent place
397                core::mem::replace(
398                    &mut timers.timers[active_timer.id].callback,
399                    CallbackVariant::Empty,
400                )
401            };
402
403            match callback {
404                CallbackVariant::Empty => (),
405                CallbackVariant::MultiFire(ref mut cb) => cb(),
406                CallbackVariant::SingleShot(cb) => {
407                    cb();
408                    timers.borrow_mut().callback_active = None;
409                    timers.borrow_mut().timers.remove(active_timer.id);
410                    continue;
411                }
412            };
413
414            let mut timers = timers.borrow_mut();
415
416            let callback_register = &mut timers.timers[active_timer.id].callback;
417
418            // only emplace back the callback if its permanent store is still Empty:
419            // if not, it means the invoked callback has restarted its own timer with a new callback
420            if matches!(callback_register, CallbackVariant::Empty) {
421                *callback_register = callback;
422            }
423
424            timers.callback_active = None;
425            let t = &mut timers.timers[active_timer.id];
426            if t.removed {
427                timers.timers.remove(active_timer.id);
428            } else {
429                t.being_activated = false;
430            }
431        }
432        any_activated
433    }
434
435    fn start_or_restart_timer(
436        &mut self,
437        id: Option<usize>,
438        mode: TimerMode,
439        duration: core::time::Duration,
440        callback: CallbackVariant,
441    ) -> usize {
442        let mut timer_data = TimerData {
443            duration,
444            mode,
445            running: false,
446            removed: false,
447            callback,
448            being_activated: false,
449        };
450        let inactive_timer_id = if let Some(id) = id {
451            self.deactivate_timer(id);
452            timer_data.being_activated = self.timers[id].being_activated;
453            self.timers[id] = timer_data;
454            id
455        } else {
456            self.timers.insert(timer_data)
457        };
458        self.activate_timer(inactive_timer_id);
459        inactive_timer_id
460    }
461
462    fn deactivate_timer(&mut self, id: usize) {
463        let mut i = 0;
464        while i < self.active_timers.len() {
465            if self.active_timers[i].id == id {
466                self.active_timers.remove(i);
467                self.timers[id].running = false;
468                debug_assert!(!self.active_timers.iter().any(|t| t.id == id));
469                break;
470            } else {
471                i += 1;
472            }
473        }
474    }
475
476    fn activate_timer(&mut self, id: usize) {
477        self.register_active_timer(ActiveTimer {
478            id,
479            timeout: self.now() + self.timers[id].duration,
480        });
481    }
482
483    fn register_active_timer(&mut self, new_active_timer: ActiveTimer) {
484        debug_assert!(!self.active_timers.iter().any(|t| t.id == new_active_timer.id));
485        let insertion_index = self
486            .active_timers
487            .partition_point(|existing_timer| existing_timer.timeout < new_active_timer.timeout);
488        self.active_timers.insert(insertion_index, new_active_timer);
489        self.timers[new_active_timer.id].running = true;
490    }
491
492    fn remove_timer(&mut self, id: usize) -> CallbackVariant {
493        self.deactivate_timer(id);
494        let t = &mut self.timers[id];
495        if t.being_activated {
496            t.removed = true;
497            CallbackVariant::Empty
498        } else {
499            self.timers.remove(id).callback
500        }
501    }
502
503    fn set_interval(&mut self, id: usize, duration: core::time::Duration) {
504        let timer = &self.timers[id];
505        if timer.running {
506            self.deactivate_timer(id);
507            self.timers[id].duration = duration;
508            self.activate_timer(id);
509        } else {
510            self.timers[id].duration = duration;
511        }
512    }
513}
514
515/// A timer list, shared so that [`Timer`] handles can point at the one they registered in
516/// without needing to know who owns it.
517pub(crate) type TimerListRc = alloc::rc::Rc<RefCell<TimerList>>;
518
519/// How a [`Timer`]'s `id` splits into the list it belongs to and its slot in that list.
520///
521/// The high part is the list's index in [`ThreadTimers::lists`] plus one, so the whole word
522/// is never zero; the low part is the slot plus one, or zero for a timer that has a list but
523/// hasn't been started. Packing both into the existing word is what keeps `Timer` one
524/// pointer wide, which the C++ ABI requires — see the assertion next to the struct.
525const LIST_SHIFT: u32 = if usize::BITS >= 64 { 32 } else { 20 };
526const SLOT_MASK: usize = (1 << LIST_SHIFT) - 1;
527
528/// This thread's timer bookkeeping: which list a handle names, and who owns the list that
529/// no context has taken over yet.
530#[derive(Default)]
531struct ThreadTimers {
532    /// The lists this thread has handed out ids for, weakly so that a context's list dies
533    /// with it. Slots are never reused: a stale id must resolve to nothing rather than to
534    /// some later list that happens to sit at the same index. Dead entries are emptied in
535    /// [`list_handle`] so they don't pin the allocation of a list that is already gone.
536    lists: Vec<alloc::rc::Weak<RefCell<TimerList>>>,
537    /// Owns the list that timers register in before this thread has a context, until the
538    /// first context adopts it. Nothing else would keep it alive: `lists` is weak, and a
539    /// `Timer` holds an id rather than a reference.
540    pending: Option<TimerListRc>,
541}
542
543crate::thread_local!(static TIMERS : RefCell<ThreadTimers> = RefCell::default());
544
545/// The handle identifying `timers` in an id, registering it on first use. Handles are the
546/// registry index plus one, so that the high part of an id is never zero.
547fn list_handle(timers: &TimerListRc) -> usize {
548    *timers.borrow_mut().handle.get_or_insert_with(|| {
549        TIMERS.with(|thread_timers| {
550            let lists = &mut thread_timers.borrow_mut().lists;
551            // A `Weak` to a list whose context is gone keeps that list's allocation alive
552            // even though its contents were dropped with the context. Registering happens
553            // once per context, so take the opportunity to release those. The slots stay
554            // occupied: reusing an index would let a stale id name a different list.
555            for entry in lists.iter_mut() {
556                if entry.strong_count() == 0 {
557                    *entry = alloc::rc::Weak::new();
558                }
559            }
560            lists.push(alloc::rc::Rc::downgrade(timers));
561            lists.len()
562        })
563    })
564}
565
566/// The list a handle refers to, or `None` once it has been dropped.
567fn list_from_handle(handle: usize) -> Option<TimerListRc> {
568    TIMERS
569        .try_with(|thread_timers| {
570            thread_timers.borrow().lists.get(handle - 1).and_then(alloc::rc::Weak::upgrade)
571        })
572        .ok()
573        .flatten()
574}
575
576/// The list that timers started on this thread register in: this thread's context's list,
577/// or the pending one when there is no context yet.
578///
579/// Returns `None` only when thread-local storage is already being torn down, hence the
580/// `try_with`: `Timer::drop` runs during thread exit.
581fn current_timers() -> Option<TimerListRc> {
582    let on_context = crate::context::GLOBAL_CONTEXT
583        .try_with(|ctx| ctx.get().map(|ctx| ctx.0.timers.clone()))
584        .ok()?;
585    if on_context.is_some() {
586        return on_context;
587    }
588    TIMERS
589        .try_with(|thread_timers| {
590            thread_timers.borrow_mut().pending.get_or_insert_with(Default::default).clone()
591        })
592        .ok()
593}
594
595/// Hands the pending list over to a context being constructed, so that timers started
596/// before this thread had one keep working — they hold a `Weak` to this very list.
597///
598/// The next context to be created starts from an empty list.
599/// Points `timers` at the context that now owns it, so its deadlines are measured on that
600/// context's clock.
601pub(crate) fn set_owning_context(timers: &TimerListRc, ctx: &crate::SlintContext) {
602    timers.borrow_mut().context = Some(ctx.downgrade());
603}
604
605pub(crate) fn take_pending_timers() -> TimerListRc {
606    TIMERS
607        .try_with(|thread_timers| thread_timers.borrow_mut().pending.take())
608        .ok()
609        .flatten()
610        .unwrap_or_default()
611}
612
613#[cfg(feature = "ffi")]
614pub(crate) mod ffi {
615    #![allow(unsafe_code)]
616
617    use super::*;
618    use core::ffi::c_void;
619
620    /// A handle to the timer `id`, which names both its list and its slot. An id of 0 means
621    /// "no timer yet", so C++ can pass one straight back to `slint_timer_start`.
622    fn timer_from_id(id: usize) -> Timer {
623        let timer = Timer::default();
624        timer.id.set(NonZeroUsize::new(id));
625        timer
626    }
627
628    /// Runs `f` on the timer `id` names, without unregistering it when the borrowed handle
629    /// goes away: C++ owns the timer and calls `slint_timer_destroy` in its destructor.
630    fn with_timer<R>(id: usize, f: impl FnOnce(&Timer) -> R) -> R {
631        let timer = timer_from_id(id);
632        let result = f(&timer);
633        timer.id.take();
634        result
635    }
636
637    struct WrapFn {
638        callback: extern "C" fn(*mut c_void),
639        user_data: *mut c_void,
640        drop_user_data: Option<extern "C" fn(*mut c_void)>,
641    }
642
643    impl Drop for WrapFn {
644        fn drop(&mut self) {
645            if let Some(x) = self.drop_user_data {
646                x(self.user_data)
647            }
648        }
649    }
650
651    impl WrapFn {
652        fn call(&self) {
653            (self.callback)(self.user_data)
654        }
655    }
656
657    /// Start a timer with the given mode, duration in millisecond and callback. A timer id may be provided (first argument).
658    /// A value of -1 for the timer id means a new timer is to be allocated.
659    /// The (new) timer id is returned.
660    /// The timer MUST be destroyed with slint_timer_destroy.
661    #[unsafe(no_mangle)]
662    pub extern "C" fn slint_timer_start(
663        id: usize,
664        mode: TimerMode,
665        duration: u64,
666        callback: extern "C" fn(*mut c_void),
667        user_data: *mut c_void,
668        drop_user_data: Option<extern "C" fn(*mut c_void)>,
669    ) -> usize {
670        let wrap = WrapFn { callback, user_data, drop_user_data };
671        let timer = timer_from_id(id);
672        if duration > i64::MAX as u64 {
673            // negative duration? stop the timer
674            timer.stop();
675        } else {
676            timer.start(mode, core::time::Duration::from_millis(duration), move || wrap.call());
677        }
678        timer.id.take().map(usize::from).unwrap_or(0)
679    }
680
681    /// Execute a callback with a delay in millisecond
682    #[unsafe(no_mangle)]
683    pub extern "C" fn slint_timer_singleshot(
684        delay: u64,
685        callback: extern "C" fn(*mut c_void),
686        user_data: *mut c_void,
687        drop_user_data: Option<extern "C" fn(*mut c_void)>,
688    ) {
689        let wrap = WrapFn { callback, user_data, drop_user_data };
690        Timer::single_shot(core::time::Duration::from_millis(delay), move || wrap.call());
691    }
692
693    /// Stop a timer and free its raw data
694    #[unsafe(no_mangle)]
695    pub extern "C" fn slint_timer_destroy(id: usize) {
696        drop(timer_from_id(id));
697    }
698
699    /// Stop a timer
700    #[unsafe(no_mangle)]
701    pub extern "C" fn slint_timer_stop(id: usize) {
702        with_timer(id, Timer::stop)
703    }
704
705    /// Restart a repeated timer
706    #[unsafe(no_mangle)]
707    pub extern "C" fn slint_timer_restart(id: usize) {
708        with_timer(id, Timer::restart)
709    }
710
711    /// Returns true if the timer is running; false otherwise.
712    #[unsafe(no_mangle)]
713    pub extern "C" fn slint_timer_running(id: usize) -> bool {
714        with_timer(id, Timer::running)
715    }
716
717    /// Returns the interval in milliseconds. 0 when the timer was never started.
718    #[unsafe(no_mangle)]
719    pub extern "C" fn slint_timer_interval(id: usize) -> u64 {
720        with_timer(id, |timer| timer.interval().as_millis() as u64)
721    }
722}
723
724/**
725```rust
726i_slint_backend_testing::init_no_event_loop();
727use slint::{Timer, TimerMode};
728use std::{rc::Rc, cell::RefCell, time::Duration};
729#[derive(Default)]
730struct SharedState {
731    timer_200: Timer,
732    timer_200_called: usize,
733    timer_500: Timer,
734    timer_500_called: usize,
735    timer_once: Timer,
736    timer_once_called: usize,
737}
738let state = Rc::new(RefCell::new(SharedState::default()));
739// Note: state will be leaked because of circular dependencies: don't do that in production
740let state_ = state.clone();
741state.borrow_mut().timer_200.start(TimerMode::Repeated, Duration::from_millis(200), move || {
742    state_.borrow_mut().timer_200_called += 1;
743});
744let state_ = state.clone();
745state.borrow_mut().timer_once.start(TimerMode::Repeated, Duration::from_millis(300), move || {
746    state_.borrow_mut().timer_once_called += 1;
747    state_.borrow().timer_once.stop();
748});
749let state_ = state.clone();
750state.borrow_mut().timer_500.start(TimerMode::Repeated, Duration::from_millis(500), move || {
751    state_.borrow_mut().timer_500_called += 1;
752});
753slint::platform::update_timers_and_animations();
754i_slint_backend_testing::mock_elapsed_time(100);
755assert_eq!(state.borrow().timer_200_called, 0);
756assert_eq!(state.borrow().timer_once_called, 0);
757assert_eq!(state.borrow().timer_500_called, 0);
758i_slint_backend_testing::mock_elapsed_time(100);
759assert_eq!(state.borrow().timer_200_called, 1);
760assert_eq!(state.borrow().timer_once_called, 0);
761assert_eq!(state.borrow().timer_500_called, 0);
762i_slint_backend_testing::mock_elapsed_time(100);
763assert_eq!(state.borrow().timer_200_called, 1);
764assert_eq!(state.borrow().timer_once_called, 1);
765assert_eq!(state.borrow().timer_500_called, 0);
766i_slint_backend_testing::mock_elapsed_time(200); // total: 500
767assert_eq!(state.borrow().timer_200_called, 2);
768assert_eq!(state.borrow().timer_once_called, 1);
769assert_eq!(state.borrow().timer_500_called, 1);
770for _ in 0..10 {
771    i_slint_backend_testing::mock_elapsed_time(100);
772}
773// total: 1500
774assert_eq!(state.borrow().timer_200_called, 7);
775assert_eq!(state.borrow().timer_once_called, 1);
776assert_eq!(state.borrow().timer_500_called, 3);
777state.borrow().timer_once.restart();
778state.borrow().timer_200.restart();
779state.borrow().timer_500.stop();
780slint::platform::update_timers_and_animations();
781i_slint_backend_testing::mock_elapsed_time(100);
782assert_eq!(state.borrow().timer_200_called, 7);
783assert_eq!(state.borrow().timer_once_called, 1);
784assert_eq!(state.borrow().timer_500_called, 3);
785slint::platform::update_timers_and_animations();
786i_slint_backend_testing::mock_elapsed_time(100);
787assert_eq!(state.borrow().timer_200_called, 8);
788assert_eq!(state.borrow().timer_once_called, 1);
789assert_eq!(state.borrow().timer_500_called, 3);
790slint::platform::update_timers_and_animations();
791i_slint_backend_testing::mock_elapsed_time(100);
792assert_eq!(state.borrow().timer_200_called, 8);
793assert_eq!(state.borrow().timer_once_called, 2);
794assert_eq!(state.borrow().timer_500_called, 3);
795slint::platform::update_timers_and_animations();
796i_slint_backend_testing::mock_elapsed_time(1000);
797slint::platform::update_timers_and_animations();
798slint::platform::update_timers_and_animations();
799// Despite 1000ms have passed, the 200 timer is only called once because we didn't call update_timers_and_animations in between
800assert_eq!(state.borrow().timer_200_called, 9);
801assert_eq!(state.borrow().timer_once_called, 2);
802assert_eq!(state.borrow().timer_500_called, 3);
803let state_ = state.clone();
804state.borrow().timer_200.start(TimerMode::SingleShot, Duration::from_millis(200), move || {
805    state_.borrow_mut().timer_200_called += 1;
806});
807for _ in 0..5 {
808    i_slint_backend_testing::mock_elapsed_time(75);
809}
810assert_eq!(state.borrow().timer_200_called, 10);
811assert_eq!(state.borrow().timer_once_called, 2);
812assert_eq!(state.borrow().timer_500_called, 3);
813state.borrow().timer_200.restart();
814for _ in 0..5 {
815    i_slint_backend_testing::mock_elapsed_time(75);
816}
817assert_eq!(state.borrow().timer_200_called, 11);
818assert_eq!(state.borrow().timer_once_called, 2);
819assert_eq!(state.borrow().timer_500_called, 3);
820
821// Test re-starting from a callback
822let state_ = state.clone();
823state.borrow_mut().timer_500.start(TimerMode::Repeated, Duration::from_millis(500), move || {
824    state_.borrow_mut().timer_500_called += 1;
825    let state__ = state_.clone();
826    state_.borrow_mut().timer_500.start(TimerMode::Repeated, Duration::from_millis(500), move || {
827        state__.borrow_mut().timer_500_called += 1000;
828    });
829    let state__ = state_.clone();
830    state_.borrow_mut().timer_200.start(TimerMode::Repeated, Duration::from_millis(200), move || {
831        state__.borrow_mut().timer_200_called += 1000;
832    });
833});
834for _ in 0..20 {
835    i_slint_backend_testing::mock_elapsed_time(100);
836}
837assert_eq!(state.borrow().timer_200_called, 7011);
838assert_eq!(state.borrow().timer_once_called, 2);
839assert_eq!(state.borrow().timer_500_called, 3004);
840
841// Test set interval
842let state_ = state.clone();
843state.borrow_mut().timer_200.start(TimerMode::Repeated, Duration::from_millis(200), move || {
844    state_.borrow_mut().timer_200_called += 1;
845});
846let state_ = state.clone();
847state.borrow_mut().timer_once.start(TimerMode::Repeated, Duration::from_millis(300), move || {
848    state_.borrow_mut().timer_once_called += 1;
849    state_.borrow().timer_once.stop();
850});
851let state_ = state.clone();
852state.borrow_mut().timer_500.start(TimerMode::Repeated, Duration::from_millis(500), move || {
853    state_.borrow_mut().timer_500_called += 1;
854});
855
856let state_ = state.clone();
857slint::platform::update_timers_and_animations();
858for _ in 0..5 {
859    i_slint_backend_testing::mock_elapsed_time(100);
860}
861slint::platform::update_timers_and_animations();
862assert_eq!(state.borrow().timer_200_called, 7013);
863assert_eq!(state.borrow().timer_once_called, 3);
864assert_eq!(state.borrow().timer_500_called, 3005);
865
866for _ in 0..20 {
867    state.borrow().timer_200.set_interval(Duration::from_millis(200 * 2));
868    state.borrow().timer_once.set_interval(Duration::from_millis(300 * 2));
869    state.borrow().timer_500.set_interval(Duration::from_millis(500 * 2));
870
871    assert_eq!(state.borrow().timer_200_called, 7013);
872    assert_eq!(state.borrow().timer_once_called, 3);
873    assert_eq!(state.borrow().timer_500_called, 3005);
874
875    i_slint_backend_testing::mock_elapsed_time(100);
876}
877
878slint::platform::update_timers_and_animations();
879for _ in 0..9 {
880    i_slint_backend_testing::mock_elapsed_time(100);
881}
882slint::platform::update_timers_and_animations();
883assert_eq!(state.borrow().timer_200_called, 7015);
884assert_eq!(state.borrow().timer_once_called, 3);
885assert_eq!(state.borrow().timer_500_called, 3006);
886
887state.borrow().timer_200.stop();
888state.borrow().timer_500.stop();
889
890state.borrow_mut().timer_once.restart();
891for _ in 0..4 {
892    i_slint_backend_testing::mock_elapsed_time(100);
893}
894assert_eq!(state.borrow().timer_once_called, 3);
895for _ in 0..4 {
896    i_slint_backend_testing::mock_elapsed_time(100);
897}
898assert_eq!(state.borrow().timer_once_called, 4);
899
900state.borrow_mut().timer_once.stop();
901i_slint_backend_testing::mock_elapsed_time(1000);
902
903assert_eq!(state.borrow().timer_200_called, 7015);
904assert_eq!(state.borrow().timer_once_called, 4);
905assert_eq!(state.borrow().timer_500_called, 3006);
906```
907 */
908#[cfg(doctest)]
909const _TIMER_TESTS: () = ();
910
911/**
912 * Test that deleting an active timer from a timer event works.
913```rust
914// There is a 200 ms timer that increase variable1
915// after 500ms, that timer is destroyed by a single shot timer,
916// and a new new timer  increase variable2
917i_slint_backend_testing::init_no_event_loop();
918use slint::{Timer, TimerMode};
919use std::{rc::Rc, cell::RefCell, time::Duration};
920#[derive(Default)]
921struct SharedState {
922    repeated_timer: Timer,
923    variable1: usize,
924    variable2: usize,
925}
926let state = Rc::new(RefCell::new(SharedState::default()));
927// Note: state will be leaked because of circular dependencies: don't do that in production
928let state_ = state.clone();
929state.borrow_mut().repeated_timer.start(TimerMode::Repeated, Duration::from_millis(200), move || {
930    state_.borrow_mut().variable1 += 1;
931});
932let state_ = state.clone();
933Timer::single_shot(Duration::from_millis(500), move || {
934    state_.borrow_mut().repeated_timer = Default::default();
935    let state = state_.clone();
936    state_.borrow_mut().repeated_timer.start(TimerMode::Repeated, Duration::from_millis(200), move || {
937        state.borrow_mut().variable2 += 1;
938    })
939} );
940i_slint_backend_testing::mock_elapsed_time(10);
941assert_eq!(state.borrow().variable1, 0);
942assert_eq!(state.borrow().variable2, 0);
943i_slint_backend_testing::mock_elapsed_time(200);
944assert_eq!(state.borrow().variable1, 1);
945assert_eq!(state.borrow().variable2, 0);
946i_slint_backend_testing::mock_elapsed_time(200);
947assert_eq!(state.borrow().variable1, 2);
948assert_eq!(state.borrow().variable2, 0);
949i_slint_backend_testing::mock_elapsed_time(100);
950// More than 500ms have elapsed, the single shot timer should have been activated, but that has no effect on variable 1 and 2
951// This should just restart the timer so that the next change should happen 200ms from now
952assert_eq!(state.borrow().variable1, 2);
953assert_eq!(state.borrow().variable2, 0);
954i_slint_backend_testing::mock_elapsed_time(110);
955assert_eq!(state.borrow().variable1, 2);
956assert_eq!(state.borrow().variable2, 0);
957i_slint_backend_testing::mock_elapsed_time(100);
958assert_eq!(state.borrow().variable1, 2);
959assert_eq!(state.borrow().variable2, 1);
960i_slint_backend_testing::mock_elapsed_time(100);
961assert_eq!(state.borrow().variable1, 2);
962assert_eq!(state.borrow().variable2, 1);
963i_slint_backend_testing::mock_elapsed_time(100);
964assert_eq!(state.borrow().variable1, 2);
965assert_eq!(state.borrow().variable2, 2);
966```
967 */
968#[cfg(doctest)]
969const _BUG3019: () = ();
970
971/**
972 * Test that starting a singleshot timer works
973```rust
974// There is a 200 ms singleshot timer that increase variable1
975i_slint_backend_testing::init_no_event_loop();
976use slint::{Timer, TimerMode};
977use std::{rc::Rc, cell::RefCell, time::Duration};
978#[derive(Default)]
979struct SharedState {
980    variable1: usize,
981}
982let state = Rc::new(RefCell::new(SharedState::default()));
983let state_ = state.clone();
984let timer = Timer::default();
985
986timer.start(TimerMode::SingleShot, Duration::from_millis(200), move || {
987    state_.borrow_mut().variable1 += 1;
988});
989
990// Singleshot timer set up and run...
991assert!(timer.running());
992i_slint_backend_testing::mock_elapsed_time(10);
993assert!(timer.running());
994assert_eq!(state.borrow().variable1, 0);
995i_slint_backend_testing::mock_elapsed_time(200);
996assert_eq!(state.borrow().variable1, 1);
997assert!(!timer.running());
998i_slint_backend_testing::mock_elapsed_time(200);
999assert_eq!(state.borrow().variable1, 1); // It's singleshot, it only triggers once!
1000assert!(!timer.running());
1001
1002// Restart a previously set up singleshot timer
1003timer.restart();
1004assert!(timer.running());
1005assert_eq!(state.borrow().variable1, 1);
1006i_slint_backend_testing::mock_elapsed_time(200);
1007assert_eq!(state.borrow().variable1, 2);
1008assert!(!timer.running());
1009i_slint_backend_testing::mock_elapsed_time(200);
1010assert_eq!(state.borrow().variable1, 2); // It's singleshot, it only triggers once!
1011assert!(!timer.running());
1012
1013// Stop a non-running singleshot timer
1014timer.stop();
1015assert!(!timer.running());
1016assert_eq!(state.borrow().variable1, 2);
1017i_slint_backend_testing::mock_elapsed_time(200);
1018assert_eq!(state.borrow().variable1, 2);
1019assert!(!timer.running());
1020i_slint_backend_testing::mock_elapsed_time(200);
1021assert_eq!(state.borrow().variable1, 2); // It's singleshot, it only triggers once!
1022assert!(!timer.running());
1023
1024// Stop a running singleshot timer
1025timer.restart();
1026assert!(timer.running());
1027assert_eq!(state.borrow().variable1, 2);
1028i_slint_backend_testing::mock_elapsed_time(10);
1029timer.stop();
1030assert!(!timer.running());
1031i_slint_backend_testing::mock_elapsed_time(200);
1032assert_eq!(state.borrow().variable1, 2);
1033assert!(!timer.running());
1034i_slint_backend_testing::mock_elapsed_time(200);
1035assert_eq!(state.borrow().variable1, 2); // It's singleshot, it only triggers once!
1036assert!(!timer.running());
1037
1038// set_interval on a non-running singleshot timer
1039timer.set_interval(Duration::from_millis(300));
1040assert!(!timer.running());
1041i_slint_backend_testing::mock_elapsed_time(1000);
1042assert_eq!(state.borrow().variable1, 2);
1043assert!(!timer.running());
1044timer.restart();
1045assert!(timer.running());
1046i_slint_backend_testing::mock_elapsed_time(200);
1047assert_eq!(state.borrow().variable1, 2);
1048assert!(timer.running());
1049i_slint_backend_testing::mock_elapsed_time(200);
1050assert_eq!(state.borrow().variable1, 3);
1051assert!(!timer.running());
1052i_slint_backend_testing::mock_elapsed_time(300);
1053assert_eq!(state.borrow().variable1, 3); // It's singleshot, it only triggers once!
1054assert!(!timer.running());
1055
1056// set_interval on a running singleshot timer
1057timer.restart();
1058assert!(timer.running());
1059assert_eq!(state.borrow().variable1, 3);
1060i_slint_backend_testing::mock_elapsed_time(290);
1061timer.set_interval(Duration::from_millis(400));
1062assert!(timer.running());
1063i_slint_backend_testing::mock_elapsed_time(200);
1064assert_eq!(state.borrow().variable1, 3);
1065assert!(timer.running());
1066i_slint_backend_testing::mock_elapsed_time(250);
1067assert_eq!(state.borrow().variable1, 4);
1068assert!(!timer.running());
1069i_slint_backend_testing::mock_elapsed_time(400);
1070assert_eq!(state.borrow().variable1, 4); // It's singleshot, it only triggers once!
1071assert!(!timer.running());
1072```
1073 */
1074#[cfg(doctest)]
1075const _SINGLESHOT_START: () = ();
1076
1077/**
1078 * Test that it's possible to start a new timer from within Drop of a timer's closure.
1079 * This may happen when a timer's closure is dropped, that closure holds the last reference
1080 * to a component, that component is destroyed, and the accesskit code schedules a reload_tree
1081 * via a single shot.
1082```rust
1083i_slint_backend_testing::init_no_event_loop();
1084use slint::{Timer, TimerMode};
1085use std::{rc::Rc, cell::Cell, time::Duration};
1086#[derive(Default)]
1087struct CapturedInClosure {
1088    last_fired: Option<Rc<Cell<bool>>>,
1089}
1090impl Drop for CapturedInClosure {
1091    fn drop(&mut self) {
1092        if let Some(last_fired) = self.last_fired.as_ref().cloned() {
1093            Timer::single_shot(Duration::from_millis(100), move || last_fired.set(true));
1094        }
1095    }
1096}
1097
1098let last_fired = Rc::new(Cell::new(false));
1099
1100let mut cap_in_clos = CapturedInClosure::default();
1101
1102let timer_to_stop = Timer::default();
1103timer_to_stop.start(TimerMode::Repeated, Duration::from_millis(100), {
1104    let last_fired = last_fired.clone();
1105    move || {
1106    cap_in_clos.last_fired = Some(last_fired.clone());
1107}});
1108
1109assert_eq!(last_fired.get(), false);
1110i_slint_backend_testing::mock_elapsed_time(110);
1111assert_eq!(last_fired.get(), false);
1112drop(timer_to_stop);
1113
1114i_slint_backend_testing::mock_elapsed_time(110);
1115assert_eq!(last_fired.get(), true);
1116```
1117 */
1118#[cfg(doctest)]
1119const _TIMER_CLOSURE_DROP_STARTS_NEW_TIMER: () = ();
1120
1121/**
1122 * Test that it's possible to set a timer's interval from within the callback.
1123```rust
1124i_slint_backend_testing::init_no_event_loop();
1125use slint::{Timer, TimerMode};
1126use std::{rc::Rc, cell::RefCell, time::Duration};
1127#[derive(Default)]
1128struct SharedState {
1129    // Note: state will be leaked because of circular dependencies: don't do that in production
1130    timer: Timer,
1131    variable1: usize,
1132}
1133let state = Rc::new(RefCell::new(SharedState::default()));
1134let state_ = state.clone();
1135state.borrow().timer.start(TimerMode::Repeated, Duration::from_millis(200), move || {
1136    state_.borrow_mut().variable1 += 1;
1137    let variable1 = state_.borrow().variable1;
1138    if variable1 == 2 {
1139        state_.borrow().timer.set_interval(Duration::from_millis(500));
1140    } else if variable1 == 3 {
1141        state_.borrow().timer.set_interval(Duration::from_millis(100));
1142    }
1143});
1144
1145assert!(state.borrow().timer.running());
1146i_slint_backend_testing::mock_elapsed_time(10);
1147assert!(state.borrow().timer.running());
1148assert_eq!(state.borrow().variable1, 0);
1149i_slint_backend_testing::mock_elapsed_time(200);
1150assert_eq!(state.borrow().variable1, 1); // fired
1151assert!(state.borrow().timer.running());
1152i_slint_backend_testing::mock_elapsed_time(180);
1153assert_eq!(state.borrow().variable1, 1);
1154assert!(state.borrow().timer.running());
1155i_slint_backend_testing::mock_elapsed_time(30);
1156assert_eq!(state.borrow().variable1, 2); // fired
1157assert!(state.borrow().timer.running());
1158// now the timer interval should be 500
1159i_slint_backend_testing::mock_elapsed_time(480);
1160assert_eq!(state.borrow().variable1, 2);
1161assert!(state.borrow().timer.running());
1162i_slint_backend_testing::mock_elapsed_time(30);
1163assert_eq!(state.borrow().variable1, 3); // fired
1164assert!(state.borrow().timer.running());
1165// now the timer interval should be 100
1166i_slint_backend_testing::mock_elapsed_time(100);
1167assert_eq!(state.borrow().variable1, 4); // fired
1168assert!(state.borrow().timer.running());
1169i_slint_backend_testing::mock_elapsed_time(100);
1170assert_eq!(state.borrow().variable1, 5); // fired
1171assert!(state.borrow().timer.running());
1172```
1173 */
1174#[cfg(doctest)]
1175const _BUG6141_SET_INTERVAL_FROM_CALLBACK: () = ();
1176
1177/**
1178 * Test that a timer can't be activated twice.
1179```rust
1180i_slint_backend_testing::init_no_event_loop();
1181use slint::{Timer, TimerMode};
1182use std::{rc::Rc, cell::Cell, time::Duration};
1183
1184let later_timer_expiration_count = Rc::new(Cell::new(0));
1185
1186let sooner_timer = Timer::default();
1187let later_timer = Rc::new(Timer::default());
1188later_timer.start(TimerMode::SingleShot, Duration::from_millis(500), {
1189    let later_timer_expiration_count = later_timer_expiration_count.clone();
1190    move || {
1191        later_timer_expiration_count.set(later_timer_expiration_count.get() + 1);
1192    }
1193});
1194
1195sooner_timer.start(TimerMode::SingleShot, Duration::from_millis(100), {
1196    let later_timer = later_timer.clone();
1197    let later_timer_expiration_count = later_timer_expiration_count.clone();
1198    move || {
1199    later_timer.start(TimerMode::SingleShot, Duration::from_millis(600), {
1200        let later_timer_expiration_count = later_timer_expiration_count.clone();
1201        move || {
1202            later_timer_expiration_count.set(later_timer_expiration_count.get() + 1);
1203        }
1204    });
1205}});
1206
1207assert_eq!(later_timer_expiration_count.get(), 0);
1208i_slint_backend_testing::mock_elapsed_time(110);
1209assert_eq!(later_timer_expiration_count.get(), 0);
1210i_slint_backend_testing::mock_elapsed_time(400);
1211assert_eq!(later_timer_expiration_count.get(), 0);
1212i_slint_backend_testing::mock_elapsed_time(800);
1213assert_eq!(later_timer_expiration_count.get(), 1);
1214```
1215 */
1216#[cfg(doctest)]
1217const _DOUBLY_REGISTER_ACTIVE_TIMER: () = ();
1218
1219/**
1220 * Test that a timer can't be activated twice.
1221```rust
1222i_slint_backend_testing::init_no_event_loop();
1223use slint::{Timer, TimerMode};
1224use std::{rc::Rc, cell::Cell, time::Duration};
1225
1226let later_timer_expiration_count = Rc::new(Cell::new(0));
1227
1228let sooner_timer = Timer::default();
1229let later_timer = Rc::new(Timer::default());
1230later_timer.start(TimerMode::Repeated, Duration::from_millis(110), {
1231    let later_timer_expiration_count = later_timer_expiration_count.clone();
1232    move || {
1233        later_timer_expiration_count.set(later_timer_expiration_count.get() + 1);
1234    }
1235});
1236
1237sooner_timer.start(TimerMode::SingleShot, Duration::from_millis(100), {
1238    let later_timer = later_timer.clone();
1239    let later_timer_expiration_count = later_timer_expiration_count.clone();
1240    move || {
1241    later_timer.start(TimerMode::Repeated, Duration::from_millis(110), {
1242        let later_timer_expiration_count = later_timer_expiration_count.clone();
1243        move || {
1244            later_timer_expiration_count.set(later_timer_expiration_count.get() + 1);
1245        }
1246    });
1247}});
1248
1249assert_eq!(later_timer_expiration_count.get(), 0);
1250i_slint_backend_testing::mock_elapsed_time(120);
1251assert_eq!(later_timer_expiration_count.get(), 1);
1252```
1253 */
1254#[cfg(doctest)]
1255const _DOUBLY_REGISTER_ACTIVE_TIMER_2: () = ();
1256
1257/**
1258 * Test that a timer that's being activated can be restarted and dropped in one go.
1259```rust
1260i_slint_backend_testing::init_no_event_loop();
1261use slint::{Timer, TimerMode};
1262use std::{cell::RefCell, rc::Rc, time::Duration};
1263
1264let destructive_timer = Rc::new(RefCell::new(Some(Timer::default())));
1265
1266destructive_timer.borrow().as_ref().unwrap().start(TimerMode::Repeated, Duration::from_millis(110), {
1267    let destructive_timer = destructive_timer.clone();
1268    move || {
1269        // start() used to reset the `being_activated` flag...
1270        destructive_timer.borrow().as_ref().unwrap().start(TimerMode::Repeated, Duration::from_millis(110), || {});
1271        // ... which would make this drop remove the timer from the timer list altogether and continued processing
1272        // of the timer would panic as the id isn't valid anymore.
1273        drop(destructive_timer.take());
1274    }
1275});
1276
1277drop(destructive_timer);
1278i_slint_backend_testing::mock_elapsed_time(120);
1279```
1280 */
1281#[cfg(doctest)]
1282const _RESTART_TIMER_BEING_ACTIVATED: () = ();
1283
1284/**
1285 * Test that a future timer can be stopped from the activation callback of an earlier timer.
1286```rust
1287i_slint_backend_testing::init_no_event_loop();
1288use slint::{Timer, TimerMode};
1289use std::{rc::Rc, cell::Cell, time::Duration};
1290
1291let later_timer_expiration_count = Rc::new(Cell::new(0));
1292
1293let sooner_timer = Timer::default();
1294let later_timer = Rc::new(Timer::default());
1295later_timer.start(TimerMode::SingleShot, Duration::from_millis(500), {
1296    let later_timer_expiration_count = later_timer_expiration_count.clone();
1297    move || {
1298        later_timer_expiration_count.set(later_timer_expiration_count.get() + 1);
1299    }
1300});
1301
1302sooner_timer.start(TimerMode::SingleShot, Duration::from_millis(100), {
1303    let later_timer = later_timer.clone();
1304    let later_timer_expiration_count = later_timer_expiration_count.clone();
1305    move || {
1306        later_timer.stop();
1307    }
1308});
1309
1310assert_eq!(later_timer_expiration_count.get(), 0);
1311assert!(later_timer.running());
1312i_slint_backend_testing::mock_elapsed_time(110);
1313assert_eq!(later_timer_expiration_count.get(), 0);
1314assert!(!later_timer.running());
1315i_slint_backend_testing::mock_elapsed_time(800);
1316assert_eq!(later_timer_expiration_count.get(), 0);
1317assert!(!later_timer.running());
1318i_slint_backend_testing::mock_elapsed_time(800);
1319i_slint_backend_testing::mock_elapsed_time(800);
1320assert_eq!(later_timer_expiration_count.get(), 0);
1321```
1322 */
1323#[cfg(doctest)]
1324const _STOP_FUTURE_TIMER_DURING_ACTIVATION_OF_EARLIER: () = ();
1325
1326/**
1327 * Test for issue #8897
1328```rust
1329use slint::TimerMode;
1330static DROP_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1331static CALL1_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1332static CALL2_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1333std::thread::spawn(move || {
1334    struct StartTimerInDrop{};
1335    impl Drop for StartTimerInDrop {
1336        fn drop(&mut self) {
1337            DROP_COUNT.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
1338            slint::Timer::single_shot(std::time::Duration::from_millis(100), move || {
1339                println!("Timer fired");
1340            });
1341            let timer = slint::Timer::default();
1342            timer.start(TimerMode::Repeated, std::time::Duration::from_millis(100), move || {
1343                println!("fired");
1344            });
1345            timer.restart();
1346            timer.stop();
1347        }
1348    }
1349
1350    thread_local! { static START_TIMER_IN_DROP: StartTimerInDrop = StartTimerInDrop {}; }
1351    let timer = START_TIMER_IN_DROP.with(|_| { });
1352    thread_local! { static TIMER2: slint::Timer = slint::Timer::default(); }
1353    TIMER2.with(|timer| {
1354        timer.start(TimerMode::Repeated, std::time::Duration::from_millis(100), move || {
1355            CALL2_COUNT.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
1356        });
1357    });
1358
1359
1360    i_slint_backend_testing::init_no_event_loop();
1361    slint::Timer::single_shot(std::time::Duration::from_millis(100), move || {
1362            CALL1_COUNT.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
1363    });
1364    i_slint_backend_testing::mock_elapsed_time(50);
1365
1366    assert_eq!(CALL1_COUNT.load(std::sync::atomic::Ordering::SeqCst), 0);
1367    assert_eq!(CALL2_COUNT.load(std::sync::atomic::Ordering::SeqCst), 0);
1368    i_slint_backend_testing::mock_elapsed_time(60);
1369    assert_eq!(CALL1_COUNT.load(std::sync::atomic::Ordering::SeqCst), 1);
1370    assert_eq!(CALL2_COUNT.load(std::sync::atomic::Ordering::SeqCst), 1);
1371    i_slint_backend_testing::mock_elapsed_time(60);
1372}).join().unwrap();
1373assert_eq!(DROP_COUNT.load(std::sync::atomic::Ordering::SeqCst), 1);
1374assert_eq!(CALL1_COUNT.load(std::sync::atomic::Ordering::SeqCst), 1);
1375assert_eq!(CALL2_COUNT.load(std::sync::atomic::Ordering::SeqCst), 1);
1376```
1377 */
1378#[cfg(doctest)]
1379const _TIMER_AT_EXIT: () = ();
1380
1381/**
1382 * A timer created from a context registers on that context: driving the current (global)
1383 * context leaves it alone, and driving its own fires it.
1384```rust
1385use i_slint_core::platform::*;
1386struct DummyBackend;
1387impl Platform for DummyBackend {
1388    fn create_window_adapter(&self) -> Result<std::rc::Rc<dyn WindowAdapter>, PlatformError> {
1389        Err(PlatformError::Other("not implemented".into()))
1390    }
1391    fn duration_since_start(&self) -> core::time::Duration {
1392        core::time::Duration::from_millis(0)
1393    }
1394}
1395
1396// Establishes the *global* context for this thread.
1397i_slint_backend_testing::init_no_event_loop();
1398// ... and a second one, which never becomes current.
1399let ctx = i_slint_core::SlintContext::new(Box::new(DummyBackend));
1400
1401let fired = std::rc::Rc::new(std::cell::Cell::new(0));
1402let fired_ = fired.clone();
1403let timer = ctx.new_timer();
1404timer.start(slint::TimerMode::Repeated, std::time::Duration::from_millis(10), move || {
1405    fired_.set(fired_.get() + 1);
1406});
1407
1408// It is not in the global context's list, so driving that one does nothing...
1409i_slint_backend_testing::mock_elapsed_time(500);
1410assert_eq!(fired.get(), 0);
1411assert_eq!(i_slint_core::timers::TimerList::next_timeout(), None);
1412
1413// ... while driving its own context fires it.
1414assert!(ctx.next_timer_timeout().is_some());
1415assert!(ctx.maybe_activate_timers(i_slint_core::animations::Instant(10_000)));
1416assert_eq!(fired.get(), 1);
1417```
1418 */
1419#[cfg(doctest)]
1420const _TIMER_ON_ITS_OWN_CONTEXT: () = ();
1421
1422/**
1423 * A timer outliving its context goes inert rather than indexing a list that no longer
1424 * exists, and can be restarted onto the current context afterwards.
1425```rust
1426use i_slint_core::platform::*;
1427struct DummyBackend;
1428impl Platform for DummyBackend {
1429    fn create_window_adapter(&self) -> Result<std::rc::Rc<dyn WindowAdapter>, PlatformError> {
1430        Err(PlatformError::Other("not implemented".into()))
1431    }
1432    fn duration_since_start(&self) -> core::time::Duration {
1433        core::time::Duration::from_millis(0)
1434    }
1435}
1436i_slint_backend_testing::init_no_event_loop();
1437
1438let fired = std::rc::Rc::new(std::cell::Cell::new(0));
1439let fired_ = fired.clone();
1440let timer = {
1441    let ctx = i_slint_core::SlintContext::new(Box::new(DummyBackend));
1442    let timer = ctx.new_timer();
1443    timer.start(slint::TimerMode::Repeated, std::time::Duration::from_millis(10), move || {
1444        fired_.set(fired_.get() + 1);
1445    });
1446    assert!(timer.running());
1447    timer
1448}; // the context, and with it the list holding the callback, is dropped here
1449
1450assert!(!timer.running());
1451assert_eq!(timer.interval(), std::time::Duration::default());
1452timer.stop();
1453timer.restart();
1454i_slint_backend_testing::mock_elapsed_time(500);
1455assert_eq!(fired.get(), 0);
1456
1457// Starting it again moves it to the current context.
1458let fired_ = fired.clone();
1459timer.start(slint::TimerMode::Repeated, std::time::Duration::from_millis(10), move || {
1460    fired_.set(fired_.get() + 1);
1461});
1462i_slint_backend_testing::mock_elapsed_time(50);
1463assert_eq!(fired.get(), 1);
1464
1465drop(timer); // must not panic
1466```
1467 */
1468#[cfg(doctest)]
1469const _TIMER_OUTLIVING_ITS_CONTEXT: () = ();