diff --git a/core/embed/rust/src/ui/component/base.rs b/core/embed/rust/src/ui/component/base.rs index 441d5ab50..a92093824 100644 --- a/core/embed/rust/src/ui/component/base.rs +++ b/core/embed/rust/src/ui/component/base.rs @@ -390,6 +390,11 @@ pub struct TimerToken(u32); impl TimerToken { /// Value of an invalid (or missing) token. pub const INVALID: TimerToken = TimerToken(0); + /// Reserved value of the animation frame timer. + pub const ANIM_FRAME: TimerToken = TimerToken(1); + + /// Starting token value + const STARTING_TOKEN: u32 = 2; pub const fn from_raw(raw: u32) -> Self { Self(raw) @@ -398,15 +403,38 @@ impl TimerToken { pub const fn into_raw(self) -> u32 { self.0 } + + pub fn next_token() -> Self { + static mut NEXT_TOKEN: TimerToken = Self(TimerToken::STARTING_TOKEN); + + // SAFETY: we are in single-threaded environment + let token = unsafe { NEXT_TOKEN }; + let next = { + if token.0 == u32::MAX { + TimerToken(Self::STARTING_TOKEN) + } else { + TimerToken(token.0 + 1) + } + }; + // SAFETY: we are in single-threaded environment + unsafe { NEXT_TOKEN = next }; + token + } } #[cfg_attr(feature = "debug", derive(ufmt::derive::uDebug))] -pub struct Timer(Option); +pub struct Timer { + token: TimerToken, + running: bool, +} impl Timer { /// Create a new timer. pub const fn new() -> Self { - Self(None) + Self { + token: TimerToken::INVALID, + running: false, + } } /// Start this timer for a given duration. @@ -414,8 +442,10 @@ impl Timer { /// Requests the internal timer token to be scheduled to `duration` from /// now. If the timer was already running, its token is rescheduled. pub fn start(&mut self, ctx: &mut EventCtx, duration: Duration) { - let token = self.0.get_or_insert_with(|| ctx.next_timer_token()); - ctx.register_timer(*token, duration); + if self.token == TimerToken::INVALID { + self.token = TimerToken::next_token(); + } + ctx.register_timer(self.token, duration); } /// Stop the timer. @@ -424,7 +454,7 @@ impl Timer { /// means that _some_ scheduled task might keep running, but this timer /// will not trigger when that task expires. pub fn stop(&mut self) { - self.0 = None; + self.running = false; } /// Check if the timer has expired. @@ -432,13 +462,12 @@ impl Timer { /// Returns `true` if the given event is a timer event and the token matches /// the internal token of this timer. pub fn is_expired(&self, event: Event) -> bool { - matches!(event, Event::Timer(token) if self.0 == Some(token)) + matches!(event, Event::Timer(token) if self.running && self.token == token) } } pub struct EventCtx { timers: Vec<(TimerToken, Duration), { Self::MAX_TIMERS }>, - next_token: u32, place_requested: bool, paint_requested: bool, anim_frame_scheduled: bool, @@ -457,17 +486,12 @@ impl EventCtx { /// How long into the future we should schedule the animation frame timer. const ANIM_FRAME_DURATION: Duration = Duration::from_millis(1); - // 0 == `TimerToken::INVALID`, - // 1 == `Self::ANIM_FRAME_TIMER`. - const STARTING_TIMER_TOKEN: u32 = 2; - /// Maximum amount of timers requested in one event tick. const MAX_TIMERS: usize = 4; pub fn new() -> Self { Self { timers: Vec::new(), - next_token: Self::STARTING_TIMER_TOKEN, place_requested: false, paint_requested: false, anim_frame_scheduled: false, @@ -572,10 +596,7 @@ impl EventCtx { #[cfg(feature = "ui_debug")] assert!(self.button_request.is_none()); // replace self with a new instance, keeping only the fields we care about - *self = Self { - next_token: self.next_token, - ..Self::new() - } + *self = Self::new(); } fn register_timer(&mut self, token: TimerToken, duration: Duration) { @@ -587,18 +608,6 @@ impl EventCtx { } } - fn next_timer_token(&mut self) -> TimerToken { - let token = TimerToken(self.next_token); - // We start again from the beginning if the token counter overflows. This would - // probably happen in case of a bug and a long-running session. Let's risk the - // collisions in such case. - self.next_token = self - .next_token - .checked_add(1) - .unwrap_or(Self::STARTING_TIMER_TOKEN); - token - } - pub fn set_transition_out(&mut self, attach_type: AttachType) { self.transition_out = Some(attach_type); }