umsh_ux_display_tracker/
boot.rs

1//! One boot splash, timed from when the panel finishes displaying it.
2//!
3//! Independent of the menu: opening About never starts this timer. The
4//! display task keeps input gated until the replacement frame is flushed.
5
6/// Boot-only presentation state, driven by monotonic milliseconds.
7#[derive(Clone, Copy, Debug)]
8pub struct BootSplash {
9    active: bool,
10    deadline_ms: Option<u64>,
11}
12
13impl BootSplash {
14    pub const fn new() -> Self {
15        Self {
16            active: true,
17            deadline_ms: None,
18        }
19    }
20
21    pub const fn is_active(&self) -> bool {
22        self.active
23    }
24
25    /// Arm once, after the initial panel flush or refresh completes.
26    pub fn shown(&mut self, now_ms: u64) {
27        if self.active && self.deadline_ms.is_none() {
28            self.deadline_ms = Some(now_ms.saturating_add(2_000));
29        }
30    }
31
32    pub const fn next_deadline(&self) -> Option<u64> {
33        self.deadline_ms
34    }
35
36    /// Returns true only when this call ends the splash.
37    pub fn dismiss(&mut self) -> bool {
38        self.deadline_ms = None;
39        core::mem::replace(&mut self.active, false)
40    }
41
42    /// Returns true once, when the two visible seconds have elapsed.
43    pub fn poll(&mut self, now_ms: u64) -> bool {
44        if self.deadline_ms.is_some_and(|deadline| now_ms >= deadline) {
45            self.dismiss()
46        } else {
47            false
48        }
49    }
50}
51
52impl Default for BootSplash {
53    fn default() -> Self {
54        Self::new()
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn the_two_visible_seconds_start_after_the_refresh() {
64        let mut splash = BootSplash::new();
65        assert!(!splash.poll(2_000));
66        assert_eq!(splash.next_deadline(), None);
67        splash.shown(2_000);
68        assert_eq!(splash.next_deadline(), Some(4_000));
69        assert!(!splash.poll(3_999));
70        // An unrelated redraw cannot extend the dwell.
71        splash.shown(3_999);
72        assert!(splash.poll(4_000));
73        assert!(!splash.is_active());
74        assert!(!splash.poll(4_001));
75        splash.shown(5_000);
76        assert_eq!(splash.next_deadline(), None);
77    }
78
79    #[test]
80    fn dismissal_is_final_even_before_the_initial_refresh_finishes() {
81        for shown in [false, true] {
82            let mut splash = BootSplash::new();
83            if shown {
84                splash.shown(100);
85            }
86            assert!(splash.dismiss());
87            assert!(!splash.dismiss());
88            splash.shown(200);
89            assert!(!splash.is_active());
90            assert_eq!(splash.next_deadline(), None);
91            assert!(!splash.poll(10_000));
92        }
93    }
94}