diff --git a/firmware/application/apps/ui_debug.cpp b/firmware/application/apps/ui_debug.cpp index 19997702..bbde342e 100644 --- a/firmware/application/apps/ui_debug.cpp +++ b/firmware/application/apps/ui_debug.cpp @@ -248,6 +248,7 @@ void ControlsSwitchesWidget::on_show() { bool ControlsSwitchesWidget::on_key(const KeyEvent key) { key_event_mask = 1 << toUType(key); + long_press_key_event_mask = switch_long_press_occurred((size_t)key) ? key_event_mask : 0; return true; } @@ -325,6 +326,14 @@ void ControlsSwitchesWidget::paint(Painter& painter) { switches_event >>= 1; } + + switches_event = long_press_key_event_mask; + for (const auto r : events_rects) { + if (switches_event & 1) + painter.fill_rectangle(r + pos, Color::cyan()); + + switches_event >>= 1; + } } void ControlsSwitchesWidget::on_frame_sync() { @@ -335,12 +344,21 @@ void ControlsSwitchesWidget::on_frame_sync() { DebugControlsView::DebugControlsView(NavigationView& nav) { add_children({ - &text_title, + &labels, &switches_widget, + &options_switches_mode, &button_done, }); - button_done.on_select = [&nav](Button&) { nav.pop(); }; + button_done.on_select = [&nav](Button&) { + switches_long_press_enable(0); + nav.pop(); + }; + + options_switches_mode.on_change = [this](size_t, OptionsField::value_t v) { + (void)v; + switches_long_press_enable(options_switches_mode.selected_index_value()); + }; } void DebugControlsView::focus() { diff --git a/firmware/application/apps/ui_debug.hpp b/firmware/application/apps/ui_debug.hpp index 3fc0c2ff..9d639512 100644 --- a/firmware/application/apps/ui_debug.hpp +++ b/firmware/application/apps/ui_debug.hpp @@ -31,6 +31,7 @@ #include "rffc507x.hpp" #include "portapack.hpp" #include "memory_map.hpp" +#include "irq_controls.hpp" #include #include @@ -220,7 +221,8 @@ class ControlsSwitchesWidget : public Widget { ControlsSwitchesWidget( Rect parent_rect) : Widget{parent_rect}, - key_event_mask(0) { + key_event_mask(0), + long_press_key_event_mask{0} { set_focusable(true); } @@ -231,6 +233,7 @@ class ControlsSwitchesWidget : public Widget { private: uint8_t key_event_mask; + uint8_t long_press_key_event_mask; MessageHandlerRegistration message_handler_frame_sync{ Message::ID::DisplayFrameSync, @@ -250,15 +253,22 @@ class DebugControlsView : public View { std::string title() const override { return "Buttons Test"; }; private: - Text text_title{ - {64, 16, 184, 16}, - "Controls State", - }; + Labels labels{ + {{8 * 8, 1 * 16}, "Controls State", Color::white()}, + {{0 * 8, 14 * 16}, "Long-Press Mode:", Color::grey()}}; ControlsSwitchesWidget switches_widget{ {80, 80, 80, 112}, }; + OptionsField options_switches_mode{ + {17 * 8, 14 * 16}, + 8, + { + {"Disabled", 0}, + {"Enabled", 0xFF}, // all KeyEvent bits to long-press mode + }}; + Button button_done{ {72, 264, 96, 24}, "Done"}; diff --git a/firmware/application/hw/debounce.cpp b/firmware/application/hw/debounce.cpp index 5e2bf51c..6e0f176a 100644 --- a/firmware/application/hw/debounce.cpp +++ b/firmware/application/hw/debounce.cpp @@ -30,7 +30,7 @@ bool Debounce::feed(const uint8_t bit) { // "Repeat" handling - simulated button release if (repeat_ctr_) { // Make sure the button is still being held continuously - if (history_ == 0xFF) { + if ((history_ == 0xFF) && !long_press_enabled_) { // Simulate button press every REPEAT_SUBSEQUENT_DELAY ticks if (--repeat_ctr_ == 0) { state_ = !state_; @@ -49,29 +49,59 @@ bool Debounce::feed(const uint8_t bit) { if ((history_ & DEBOUNCE_MASK) == DEBOUNCE_MASK) { state_ = 1; held_time_ = 0; + + // If long_press_enabled_, state() function masks the button press until it's released + // or until LONG_PRESS_DELAY is reached + if (long_press_enabled_) { + pulse_upon_release_ = true; + return false; + } return true; } } else { // Previous button state was 1 (pressed); // Has button been released for DEBOUNCE_COUNT ticks? if ((history_ & DEBOUNCE_MASK) == 0) { - state_ = 0; + // Button has been released when long_press_enabled_ and before LONG_PRESS_DELAY was reached; + // allow state() function to finally return a single press indication + // (in long press mode, apps won't see button press until the button is released) + if (pulse_upon_release_) { + // leaving state_==1 for one cycle + pulse_upon_release_ = 0; + } else { + state_ = 0; + } + + // Reset long_press_occurred_ flag after button is released + long_press_occurred_ = false; return true; } - // Repeat support is limited to the 4 directional buttons - if (repeat_enabled_) { - // Has button been held continuously for DEBOUNCE_REPEAT_DELAY? - if (history_ == 0xFF) { - if (++held_time_ == REPEAT_INITIAL_DELAY) { + // Has button been held continuously? + if (history_ == 0xFF) { + held_time_++; + if (pulse_upon_release_) { + // Button is being held down and long_press support is enabled for this key: + // if LONG_PRESS_DELAY is reached then finally report that switch is pressed and set flag + // indicating it was a LONG press + // (note that repease_support and long_press support are mutually exclusive) + if (held_time_ == LONG_PRESS_DELAY) { + long_press_occurred_ = true; + pulse_upon_release_ = 0; + held_time_ = 0; + return true; + } + } else if (repeat_enabled_ && !long_press_enabled_) { + // Repeat support -- 4 directional buttons only (unless long_press is enabled) + if (held_time_ == REPEAT_INITIAL_DELAY) { // Delay reached; trigger repeat code on NEXT tick repeat_ctr_ = 1; held_time_ = 0; } - } else { - // Button not continuously pressed; reset counter - held_time_ = 0; } + } else { + // Button not continuously pressed; reset counter + held_time_ = 0; } } return false; diff --git a/firmware/application/hw/debounce.hpp b/firmware/application/hw/debounce.hpp index 14ba29e9..f39efa77 100644 --- a/firmware/application/hw/debounce.hpp +++ b/firmware/application/hw/debounce.hpp @@ -31,25 +31,39 @@ // # of timer0 ticks before a held button starts being counted as repeated presses #define REPEAT_INITIAL_DELAY 250 #define REPEAT_SUBSEQUENT_DELAY 92 +#define LONG_PRESS_DELAY 1000 class Debounce { public: bool feed(const uint8_t bit); uint8_t state() const { - return state_; + return (pulse_upon_release_) ? 0 : state_; } void enable_repeat() { repeat_enabled_ = true; } + void set_long_press_support(bool v) { + long_press_enabled_ = v; + } + + bool long_press_occurred() { + bool v = long_press_occurred_; + long_press_occurred_ = false; + return v; + } + private: uint8_t history_{0}; uint8_t state_{0}; bool repeat_enabled_{0}; uint16_t repeat_ctr_{0}; uint16_t held_time_{0}; + bool pulse_upon_release_{0}; + bool long_press_enabled_{0}; + bool long_press_occurred_{0}; }; #endif /*__DEBOUNCE_H__*/ diff --git a/firmware/application/irq_controls.cpp b/firmware/application/irq_controls.cpp index bd31e378..3d1be15e 100644 --- a/firmware/application/irq_controls.cpp +++ b/firmware/application/irq_controls.cpp @@ -195,16 +195,33 @@ void controls_init() { SwitchesState get_switches_state() { SwitchesState result; + + // Right, Left, Down, Up, & Select switches for (size_t i = 0; i < result.size() - 1; i++) { // TODO: Ignore multiple keys at the same time? result[i] = switch_debounce[i].state(); } - result[result.size() - 1] = switch_debounce[switch_debounce.size() - 1].state(); - + // Grab Dfu switch from bit 7 and return in bit 5 so that all switches are grouped together in this 6-bit result + result[(size_t)Switch::Dfu] = switch_debounce[7].state(); return result; } +// Configure which switches support long press (note those switches will not support Repeat function) +void switches_long_press_enable(SwitchesState switches_long_press_enabled) { + // Right, Left, Down, Up, & Select switches + for (size_t i = 0; i < switches_long_press_enabled.size() - 1; i++) { + switch_debounce[i].set_long_press_support(switches_long_press_enabled[i]); + } + + // Dfu switch + switch_debounce[7].set_long_press_support(switches_long_press_enabled[(size_t)Switch::Dfu]); +} + +bool switch_long_press_occurred(size_t v) { + return (v == (size_t)Switch::Dfu) ? switch_debounce[7].long_press_occurred() : switch_debounce[v].long_press_occurred(); +} + EncoderPosition get_encoder_position() { return encoder_position; } diff --git a/firmware/application/irq_controls.hpp b/firmware/application/irq_controls.hpp index 85ebc273..a4ea0139 100644 --- a/firmware/application/irq_controls.hpp +++ b/firmware/application/irq_controls.hpp @@ -27,6 +27,7 @@ #include "touch.hpp" +// Must be same values as in ui::KeyEvent enum class Switch { Right = 0, Left = 1, @@ -44,6 +45,8 @@ void controls_init(); SwitchesState get_switches_state(); EncoderPosition get_encoder_position(); touch::Frame get_touch_frame(); +void switches_long_press_enable(SwitchesState v); +bool switch_long_press_occurred(size_t v); namespace control { namespace debug {