From e869e1b146c328a6f7da6a2993d5aa1cb8a63fab Mon Sep 17 00:00:00 2001 From: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com> Date: Fri, 6 Jun 2025 22:06:45 -0400 Subject: [PATCH] button thread cleanup --- src/ButtonThread.cpp | 44 +++++++++++++++++++++++++++++ src/ButtonThread.h | 9 ++++-- src/graphics/Screen.cpp | 12 +++++++- src/input/InputBroker.h | 3 ++ src/modules/CannedMessageModule.cpp | 35 +++++++++++++++++++++-- 5 files changed, 98 insertions(+), 5 deletions(-) diff --git a/src/ButtonThread.cpp b/src/ButtonThread.cpp index 4c6a7d4c4..8c62a7141 100644 --- a/src/ButtonThread.cpp +++ b/src/ButtonThread.cpp @@ -13,6 +13,7 @@ #include "modules/ExternalNotificationModule.h" #include "power.h" #include "sleep.h" +#include "input/InputBroker.h" #ifdef ARCH_PORTDUINO #include "platform/portduino/PortduinoGlue.h" #endif @@ -262,6 +263,48 @@ int32_t ButtonThread::runOnce() #endif if (btnEvent != BUTTON_EVENT_NONE) { +#if HAS_SCREEN + switch (btnEvent) { + case BUTTON_EVENT_PRESSED: { + LOG_BUTTON("press!"); + + // Play boop sound for every button press + playBoop(); + + // Forward single press to InputBroker (but NOT as DOWN/SELECT, just forward a "button press" event) + if (inputBroker) { + InputEvent evt = { "button", INPUT_BROKER_MSG_BUTTON_PRESSED, 0, 0, 0 }; + inputBroker->injectInputEvent(&evt); + } + + // Start tracking for potential combination + waitingForLongPress = true; + shortPressTime = millis(); + + break; + } + case BUTTON_EVENT_LONG_PRESSED: { + LOG_BUTTON("Long press!"); + + // Play beep sound + playBeep(); + + // Forward long press to InputBroker (but NOT as DOWN/SELECT, just forward a "button long press" event) + if (inputBroker) { + InputEvent evt = { "button", INPUT_BROKER_MSG_BUTTON_LONG_PRESSED, 0, 0, 0 }; + inputBroker->injectInputEvent(&evt); + } + + waitingForLongPress = false; + break; + } + default: + // Ignore all other events on screen devices + break; + } + btnEvent = BUTTON_EVENT_NONE; +#else + // On devices without screen: full legacy logic switch (btnEvent) { case BUTTON_EVENT_PRESSED: { LOG_BUTTON("press!"); @@ -489,6 +532,7 @@ int32_t ButtonThread::runOnce() break; } btnEvent = BUTTON_EVENT_NONE; +#endif } return 50; diff --git a/src/ButtonThread.h b/src/ButtonThread.h index 1fbc38672..e4bbba8f0 100644 --- a/src/ButtonThread.h +++ b/src/ButtonThread.h @@ -8,8 +8,13 @@ #define BUTTON_CLICK_MS 250 #endif -#ifndef BUTTON_LONGPRESS_MS -#define BUTTON_LONGPRESS_MS 5000 +#ifdef HAS_SCREEN + #undef BUTTON_LONGPRESS_MS + #define BUTTON_LONGPRESS_MS 500 +#else + #ifndef BUTTON_LONGPRESS_MS + #define BUTTON_LONGPRESS_MS 5000 + #endif #endif #ifndef BUTTON_TOUCH_MS diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 21cf32283..9c150c14e 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1781,7 +1781,6 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event) int Screen::handleInputEvent(const InputEvent *event) { - #if defined(DISPLAY_CLOCK_FRAME) // For the T-Watch, intercept touches to the 'toggle digital/analog watch face' button uint8_t watchFaceFrame = error_code ? 1 : 0; @@ -1812,6 +1811,17 @@ int Screen::handleInputEvent(const InputEvent *event) inputIntercepted = true; } + // Only allow BUTTON_PRESSED and BUTTON_LONG_PRESSED to trigger frame changes if no module is handling input + if (!inputIntercepted) { + if (event->inputEvent == INPUT_BROKER_MSG_BUTTON_PRESSED) { + showNextFrame(); + return 0; + } else if (event->inputEvent == INPUT_BROKER_MSG_BUTTON_LONG_PRESSED) { + // Optional: Define alternate screen action or no-op + return 0; + } + } + // If no modules are using the input, move between frames if (!inputIntercepted) { if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) diff --git a/src/input/InputBroker.h b/src/input/InputBroker.h index 3278a5a73..6948390d2 100644 --- a/src/input/InputBroker.h +++ b/src/input/InputBroker.h @@ -21,6 +21,8 @@ #define INPUT_BROKER_MSG_BLUETOOTH_TOGGLE 0xAA #define INPUT_BROKER_MSG_TAB 0x09 #define INPUT_BROKER_MSG_EMOTE_LIST 0x8F +#define INPUT_BROKER_MSG_BUTTON_PRESSED 0xe1 +#define INPUT_BROKER_MSG_BUTTON_LONG_PRESSED 0xe2 typedef struct _InputEvent { const char *source; @@ -37,6 +39,7 @@ class InputBroker : public Observable public: InputBroker(); void registerSource(Observable *source); + void injectInputEvent(const InputEvent *event) { handleInputEvent(event); } protected: int handleInputEvent(const InputEvent *event); diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 85eb8a927..fd8228c1f 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -292,7 +292,9 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) switch (runState) { // Node/Channel destination selection mode: Handles character search, arrows, select, cancel, backspace case CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION: - return handleDestinationSelectionInput(event, isUp, isDown, isSelect); // All allowed input for this state + if (handleDestinationSelectionInput(event, isUp, isDown, isSelect)) + return 1; + return 0; // prevent fall-through to selector input // Free text input mode: Handles character input, cancel, backspace, select, etc. case CANNED_MESSAGE_RUN_STATE_FREETEXT: @@ -408,6 +410,15 @@ bool CannedMessageModule::handleTabSwitch(const InputEvent *event) int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event, bool isUp, bool isDown, bool isSelect) { + // Override isDown and isSelect ONLY for destination selector behavior + if (runState == CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION) { + if (event->inputEvent == INPUT_BROKER_MSG_BUTTON_PRESSED) { + isDown = true; + } else if (event->inputEvent == INPUT_BROKER_MSG_BUTTON_LONG_PRESSED) { + isSelect = true; + } + } + if (event->kbchar >= 32 && event->kbchar <= 126 && !isUp && !isDown && event->inputEvent != static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT) && event->inputEvent != static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT) && @@ -503,6 +514,15 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event bool CannedMessageModule::handleMessageSelectorInput(const InputEvent *event, bool isUp, bool isDown, bool isSelect) { + // Override isDown and isSelect ONLY for canned message list behavior + if (runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) { + if (event->inputEvent == INPUT_BROKER_MSG_BUTTON_PRESSED) { + isDown = true; + } else if (event->inputEvent == INPUT_BROKER_MSG_BUTTON_LONG_PRESSED) { + isSelect = true; + } + } + if (runState == CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION) return false; @@ -591,7 +611,6 @@ bool CannedMessageModule::handleMessageSelectorInput(const InputEvent *event, bo return handled; } - bool CannedMessageModule::handleFreeTextInput(const InputEvent *event) { // Always process only if in FREETEXT mode @@ -732,9 +751,18 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event) int CannedMessageModule::handleEmotePickerInput(const InputEvent *event) { int numEmotes = graphics::numEmotes; + + // Override isDown and isSelect ONLY for emote picker behavior bool isUp = isUpEvent(event); bool isDown = isDownEvent(event); bool isSelect = isSelectEvent(event); + if (runState == CANNED_MESSAGE_RUN_STATE_EMOTE_PICKER) { + if (event->inputEvent == INPUT_BROKER_MSG_BUTTON_PRESSED) { + isDown = true; + } else if (event->inputEvent == INPUT_BROKER_MSG_BUTTON_LONG_PRESSED) { + isSelect = true; + } + } // Scroll emote list if (isUp && emotePickerIndex > 0) { @@ -747,6 +775,7 @@ int CannedMessageModule::handleEmotePickerInput(const InputEvent *event) screen->forceDisplay(); return 1; } + // Select emote: insert into freetext at cursor and return to freetext if (isSelect) { String label = graphics::emotes[emotePickerIndex].label; @@ -761,12 +790,14 @@ int CannedMessageModule::handleEmotePickerInput(const InputEvent *event) screen->forceDisplay(); return 1; } + // Cancel returns to freetext if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL)) { runState = CANNED_MESSAGE_RUN_STATE_FREETEXT; screen->forceDisplay(); return 1; } + return 0; }