mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-04 05:42:34 +00:00
refactor(core/t2b1): cleaner factoring of ignore-button logic
also does not require timer
This commit is contained in:
parent
88a76b3063
commit
ab436bfbc8
@ -394,6 +394,13 @@ impl Component for ButtonController {
|
|||||||
// State machine for the ButtonController
|
// State machine for the ButtonController
|
||||||
// We are matching event with `Event::Button` for a button action
|
// We are matching event with `Event::Button` for a button action
|
||||||
// and `Event::Timer` for getting the expiration of HTC.
|
// and `Event::Timer` for getting the expiration of HTC.
|
||||||
|
|
||||||
|
if let Some(ignore_btn_delay) = &mut self.ignore_btn_delay {
|
||||||
|
// Filter out opposite buttons after some delay
|
||||||
|
if ignore_btn_delay.filter_event(event) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
match event {
|
match event {
|
||||||
Event::Button(button_event) => {
|
Event::Button(button_event) => {
|
||||||
let (new_state, event) = match self.state {
|
let (new_state, event) = match self.state {
|
||||||
@ -404,9 +411,6 @@ impl Component for ButtonController {
|
|||||||
// ↓ _ | _ ↓
|
// ↓ _ | _ ↓
|
||||||
// Initial button press will set the timer for second button,
|
// Initial button press will set the timer for second button,
|
||||||
// and after some delay, it will become un-clickable
|
// and after some delay, it will become un-clickable
|
||||||
if let Some(ignore_btn_delay) = &mut self.ignore_btn_delay {
|
|
||||||
ignore_btn_delay.handle_button_press(ctx, which);
|
|
||||||
}
|
|
||||||
(
|
(
|
||||||
ButtonState::OneDown(which),
|
ButtonState::OneDown(which),
|
||||||
match which {
|
match which {
|
||||||
@ -432,33 +436,16 @@ impl Component for ButtonController {
|
|||||||
// ▲ * | * ▲
|
// ▲ * | * ▲
|
||||||
ButtonEvent::ButtonReleased(b) if b == which_down => match which_down {
|
ButtonEvent::ButtonReleased(b) if b == which_down => match which_down {
|
||||||
// ▲ *
|
// ▲ *
|
||||||
// Trigger the button and make the second one clickable in all cases
|
|
||||||
PhysicalButton::Left => {
|
PhysicalButton::Left => {
|
||||||
if let Some(ignore_btn_delay) = &mut self.ignore_btn_delay {
|
|
||||||
ignore_btn_delay.make_button_clickable(ButtonPos::Right);
|
|
||||||
}
|
|
||||||
// _ _
|
|
||||||
(ButtonState::Nothing, self.left_btn.maybe_trigger(ctx))
|
(ButtonState::Nothing, self.left_btn.maybe_trigger(ctx))
|
||||||
}
|
}
|
||||||
// * ▲
|
// * ▲
|
||||||
PhysicalButton::Right => {
|
PhysicalButton::Right => {
|
||||||
if let Some(ignore_btn_delay) = &mut self.ignore_btn_delay {
|
|
||||||
ignore_btn_delay.make_button_clickable(ButtonPos::Left);
|
|
||||||
}
|
|
||||||
// _ _
|
|
||||||
(ButtonState::Nothing, self.right_btn.maybe_trigger(ctx))
|
(ButtonState::Nothing, self.right_btn.maybe_trigger(ctx))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// * ▼ | ▼ *
|
// * ▼ | ▼ *
|
||||||
ButtonEvent::ButtonPressed(b) if b != which_down => {
|
ButtonEvent::ButtonPressed(b) if b != which_down => {
|
||||||
// Buttons may be non-clickable (caused by long-holding the other
|
|
||||||
// button)
|
|
||||||
if let Some(ignore_btn_delay) = &self.ignore_btn_delay {
|
|
||||||
if ignore_btn_delay.ignore_button(b) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ↓ ↓
|
|
||||||
if self.handle_middle_button {
|
if self.handle_middle_button {
|
||||||
self.got_pressed(ctx, ButtonPos::Middle);
|
self.got_pressed(ctx, ButtonPos::Middle);
|
||||||
self.middle_hold_started(ctx);
|
self.middle_hold_started(ctx);
|
||||||
@ -505,10 +492,7 @@ impl Component for ButtonController {
|
|||||||
ButtonEvent::ButtonReleased(b) if b != which_up => {
|
ButtonEvent::ButtonReleased(b) if b != which_up => {
|
||||||
// _ _
|
// _ _
|
||||||
// Both buttons need to be clickable now
|
// Both buttons need to be clickable now
|
||||||
if let Some(ignore_btn_delay) = &mut self.ignore_btn_delay {
|
self.ignore_btn_delay.as_mut().map(IgnoreButtonDelay::reset);
|
||||||
ignore_btn_delay.make_button_clickable(ButtonPos::Left);
|
|
||||||
ignore_btn_delay.make_button_clickable(ButtonPos::Right);
|
|
||||||
}
|
|
||||||
if self.handle_middle_button {
|
if self.handle_middle_button {
|
||||||
(ButtonState::Nothing, self.middle_btn.maybe_trigger(ctx))
|
(ButtonState::Nothing, self.middle_btn.maybe_trigger(ctx))
|
||||||
} else {
|
} else {
|
||||||
@ -556,9 +540,6 @@ impl Component for ButtonController {
|
|||||||
}
|
}
|
||||||
// Timer - handle clickable properties and HoldToConfirm expiration
|
// Timer - handle clickable properties and HoldToConfirm expiration
|
||||||
Event::Timer(token) => {
|
Event::Timer(token) => {
|
||||||
if let Some(ignore_btn_delay) = &mut self.ignore_btn_delay {
|
|
||||||
ignore_btn_delay.handle_timer_token(token);
|
|
||||||
}
|
|
||||||
if let Some(pos) = self.handle_long_press_timer_token(token) {
|
if let Some(pos) = self.handle_long_press_timer_token(token) {
|
||||||
return Some(ButtonControllerMsg::LongPressed(pos));
|
return Some(ButtonControllerMsg::LongPressed(pos));
|
||||||
}
|
}
|
||||||
@ -589,82 +570,110 @@ impl Component for ButtonController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
enum IgnoreButtonDelayState {
|
||||||
|
// All buttons are clickable.
|
||||||
|
Unlocked,
|
||||||
|
// Button was pressed down at a given time.
|
||||||
|
ButtonDown(PhysicalButton, Instant),
|
||||||
|
// Button is locked, events for all other buttons are ignored.
|
||||||
|
ButtonLocked(PhysicalButton),
|
||||||
|
}
|
||||||
|
|
||||||
/// When one button is pressed, the other one becomes un-clickable after some
|
/// When one button is pressed, the other one becomes un-clickable after some
|
||||||
/// small time period. This is to prevent accidental clicks when user is holding
|
/// small time period. This is to prevent accidental clicks when user is holding
|
||||||
/// the button to automatically move through items.
|
/// the button to automatically move through items.
|
||||||
struct IgnoreButtonDelay {
|
struct IgnoreButtonDelay {
|
||||||
/// How big is the delay after the button is inactive
|
/// How big is the delay after the button is inactive
|
||||||
delay: Duration,
|
delay: Duration,
|
||||||
/// Whether left button is currently clickable
|
/// Lock state
|
||||||
left_clickable: bool,
|
state: IgnoreButtonDelayState,
|
||||||
/// Whether right button is currently clickable
|
/// State of an ignored button that was pressed down while another button
|
||||||
right_clickable: bool,
|
/// was locked. We need to filter out its release event even after the
|
||||||
/// Timer for setting the left_clickable
|
/// lock expires, otherwise a spurious Up event with no corresponding
|
||||||
left_clickable_timer: Option<TimerToken>,
|
/// Down event will escape.
|
||||||
/// Timer for setting the right_clickable
|
ignored_button_needs_release: Option<PhysicalButton>,
|
||||||
right_clickable_timer: Option<TimerToken>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IgnoreButtonDelay {
|
impl IgnoreButtonDelay {
|
||||||
pub fn new(delay_ms: u32) -> Self {
|
pub fn new(delay_ms: u32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
delay: Duration::from_millis(delay_ms),
|
delay: Duration::from_millis(delay_ms),
|
||||||
left_clickable: true,
|
state: IgnoreButtonDelayState::Unlocked,
|
||||||
right_clickable: true,
|
ignored_button_needs_release: None,
|
||||||
left_clickable_timer: None,
|
|
||||||
right_clickable_timer: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_button_clickable(&mut self, pos: ButtonPos) {
|
/// Filter a ButtonPressed event.
|
||||||
match pos {
|
fn filter_pressed(&mut self, button: PhysicalButton) -> bool {
|
||||||
ButtonPos::Left => {
|
match self.state {
|
||||||
self.left_clickable = true;
|
IgnoreButtonDelayState::Unlocked => {
|
||||||
self.left_clickable_timer = None;
|
// A new button was pressed. Register ButtonDown, do not filter.
|
||||||
|
self.state = IgnoreButtonDelayState::ButtonDown(button, Instant::now());
|
||||||
|
false
|
||||||
}
|
}
|
||||||
ButtonPos::Right => {
|
IgnoreButtonDelayState::ButtonDown(b, ..) if b != button => {
|
||||||
self.right_clickable = true;
|
// A different button was pressed before locking the first one.
|
||||||
self.right_clickable_timer = None;
|
// Unlock, do not filter.
|
||||||
|
self.state = IgnoreButtonDelayState::Unlocked;
|
||||||
|
false
|
||||||
}
|
}
|
||||||
ButtonPos::Middle => {}
|
IgnoreButtonDelayState::ButtonLocked(b) if b != button => {
|
||||||
|
// A button is already locked, and another button was pressed.
|
||||||
|
// Track that this new button needs release, and filter the event.
|
||||||
|
self.ignored_button_needs_release = Some(button);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
// Allow all other events.
|
||||||
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_button_press(&mut self, ctx: &mut EventCtx, button: PhysicalButton) {
|
/// Filter a ButtonReleased event.
|
||||||
if matches!(button, PhysicalButton::Left) {
|
fn filter_released(&mut self, button: PhysicalButton) -> bool {
|
||||||
self.right_clickable_timer = Some(ctx.request_timer(self.delay));
|
if matches!(self.ignored_button_needs_release, Some(b) if b == button) {
|
||||||
}
|
// Regardless of lock state, the ignored button that was previously pressed
|
||||||
if matches!(button, PhysicalButton::Right) {
|
// is now released. Clear the needs_release state and filter the event.
|
||||||
self.left_clickable_timer = Some(ctx.request_timer(self.delay));
|
self.ignored_button_needs_release = None;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ignore_button(&self, button: PhysicalButton) -> bool {
|
|
||||||
if matches!(button, PhysicalButton::Left) && !self.left_clickable {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if matches!(button, PhysicalButton::Right) && !self.right_clickable {
|
match self.state {
|
||||||
return true;
|
IgnoreButtonDelayState::ButtonDown(b, ..) if b == button => {
|
||||||
|
// The button which was down was released. Unlock.
|
||||||
|
self.state = IgnoreButtonDelayState::Unlocked;
|
||||||
}
|
}
|
||||||
|
IgnoreButtonDelayState::ButtonLocked(b) if b == button => {
|
||||||
|
// The locked button was released. Unlock.
|
||||||
|
self.state = IgnoreButtonDelayState::Unlocked;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_timer_token(&mut self, token: TimerToken) {
|
/// Filter an incoming event
|
||||||
if self.left_clickable_timer == Some(token) {
|
///
|
||||||
self.left_clickable = false;
|
/// If the return value is `true`, the event is filtered and should not be
|
||||||
self.left_clickable_timer = None;
|
/// processed further. Otherwise, the event should be processed as usual.
|
||||||
|
pub fn filter_event(&mut self, event: Event) -> bool {
|
||||||
|
// resolve delay elapsed event first
|
||||||
|
if let IgnoreButtonDelayState::ButtonDown(button, last_ms) = self.state {
|
||||||
|
let elapsed = Instant::now().saturating_duration_since(last_ms);
|
||||||
|
if elapsed >= self.delay {
|
||||||
|
self.state = IgnoreButtonDelayState::ButtonLocked(button);
|
||||||
}
|
}
|
||||||
if self.right_clickable_timer == Some(token) {
|
}
|
||||||
self.right_clickable = false;
|
|
||||||
self.right_clickable_timer = None;
|
match event {
|
||||||
|
Event::Button(ButtonEvent::ButtonPressed(button)) => self.filter_pressed(button),
|
||||||
|
Event::Button(ButtonEvent::ButtonReleased(button)) => self.filter_released(button),
|
||||||
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
self.left_clickable = true;
|
self.state = IgnoreButtonDelayState::Unlocked;
|
||||||
self.right_clickable = true;
|
self.ignored_button_needs_release = None;
|
||||||
self.left_clickable_timer = None;
|
|
||||||
self.right_clickable_timer = None;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user