From 5b5f9c62b5d0afc691487da33eed4446fded04a2 Mon Sep 17 00:00:00 2001 From: todd-herbert Date: Sun, 7 Apr 2024 02:04:26 +1300 Subject: [PATCH] Remap backlight toggle and touch button (#3560) * Update E-Ink display after sending adhoc ping or disable/enable GPS * Resume display updates when touch button pressed * Use touch hold as modifier; change double-click behavior for user button * Fix preprocessor exclusions * Purge backlight behavior * Distinguish between 3x and 4x multi-presses * Touch button considers "Wake screen on tap or motion" user-setting * Don't assume device has BUTTON_PIN * Rename misleading method --- src/ButtonThread.cpp | 84 ++++++++++++++++++++++++++++++++--------- src/ButtonThread.h | 10 +++-- src/graphics/Screen.cpp | 25 +++++++++++- src/graphics/Screen.h | 4 +- 4 files changed, 99 insertions(+), 24 deletions(-) diff --git a/src/ButtonThread.cpp b/src/ButtonThread.cpp index a1f0170e8..069a92308 100644 --- a/src/ButtonThread.cpp +++ b/src/ButtonThread.cpp @@ -48,7 +48,7 @@ ButtonThread::ButtonThread() : OSThread("Button") userButton.setPressMs(c_longPressTime); userButton.setDebounceMs(1); userButton.attachDoubleClick(userButtonDoublePressed); - userButton.attachMultiClick(userButtonMultiPressed); + userButton.attachMultiClick(userButtonMultiPressed, this); // Reference to instance: get click count from non-static OneButton #ifndef T_DECK // T-Deck immediately wakes up after shutdown, so disable this function userButton.attachLongPressStart(userButtonPressedLongStart); userButton.attachLongPressStop(userButtonPressedLongStop); @@ -86,7 +86,8 @@ ButtonThread::ButtonThread() : OSThread("Button") #ifdef BUTTON_PIN_TOUCH userButtonTouch = OneButton(BUTTON_PIN_TOUCH, true, true); - userButtonTouch.attachClick(touchPressed); + userButtonTouch.setPressMs(400); + userButtonTouch.attachLongPressStart(touchPressedLongStart); // Better handling with longpress than click? wakeOnIrq(BUTTON_PIN_TOUCH, FALLING); #endif } @@ -138,26 +139,42 @@ int32_t ButtonThread::runOnce() case BUTTON_EVENT_DOUBLE_PRESSED: { LOG_BUTTON("Double press!\n"); -#if defined(USE_EINK) && defined(PIN_EINK_EN) - digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW); -#endif service.refreshLocalMeshNode(); service.sendNetworkPing(NODENUM_BROADCAST, true); - if (screen) + if (screen) { screen->print("Sent ad-hoc ping\n"); - break; - } -#if HAS_GPS - case BUTTON_EVENT_MULTI_PRESSED: { - LOG_BUTTON("Multi press!\n"); - if (!config.device.disable_triple_click && (gps != nullptr)) { - gps->toggleGpsMode(); - if (screen) - screen->forceDisplay(); + screen->forceDisplay(true); // Force a new UI frame, then force an EInk update } break; } + + case BUTTON_EVENT_MULTI_PRESSED: { + LOG_BUTTON("Mulitipress! %hux\n", multipressClickCount); + switch (multipressClickCount) { +#if HAS_GPS + // 3 clicks: toggle GPS + case 3: + if (!config.device.disable_triple_click && (gps != nullptr)) { + gps->toggleGpsMode(); + if (screen) + screen->forceDisplay(true); // Force a new UI frame, then force an EInk update + } + break; #endif +#if defined(USE_EINK) && defined(PIN_EINK_EN) // i.e. T-Echo + // 4 clicks: toggle backlight + case 4: + digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW); + break; +#endif + // No valid multipress action + default: + break; + } // end switch: click count + + break; + } // end multipress event + case BUTTON_EVENT_LONG_PRESSED: { LOG_BUTTON("Long press!\n"); powerFSM.trigger(EVENT_PRESS); @@ -176,12 +193,24 @@ int32_t ButtonThread::runOnce() power->shutdown(); break; } - case BUTTON_EVENT_TOUCH_PRESSED: { + +#ifdef BUTTON_PIN_TOUCH + case BUTTON_EVENT_TOUCH_LONG_PRESSED: { LOG_BUTTON("Touch press!\n"); - if (screen) - screen->forceDisplay(); + if (config.display.wake_on_tap_or_motion) { + if (screen) { + // Wake if asleep + if (powerFSM.getState() == &stateDARK) + powerFSM.trigger(EVENT_PRESS); + + // Update display (legacy behaviour) + screen->forceDisplay(); + } + } break; } +#endif // BUTTON_PIN_TOUCH + default: break; } @@ -206,6 +235,25 @@ void ButtonThread::wakeOnIrq(int irq, int mode) FALLING); } +// Static callback +void ButtonThread::userButtonMultiPressed(void *callerThread) +{ + // Grab click count from non-static button, while the info is still valid + ButtonThread *thread = (ButtonThread *)callerThread; + thread->storeClickCount(); + + // Then handle later, in the usual way + btnEvent = BUTTON_EVENT_MULTI_PRESSED; +} + +// Non-static method, runs during callback. Grabs info while still valid +void ButtonThread::storeClickCount() +{ +#ifdef BUTTON_PIN + multipressClickCount = userButton.getNumberClicks(); +#endif +} + void ButtonThread::userButtonPressedLongStart() { if (millis() > c_holdOffTime) { diff --git a/src/ButtonThread.h b/src/ButtonThread.h index 554c1f0c4..3f177302d 100644 --- a/src/ButtonThread.h +++ b/src/ButtonThread.h @@ -17,11 +17,12 @@ class ButtonThread : public concurrency::OSThread BUTTON_EVENT_MULTI_PRESSED, BUTTON_EVENT_LONG_PRESSED, BUTTON_EVENT_LONG_RELEASED, - BUTTON_EVENT_TOUCH_PRESSED + BUTTON_EVENT_TOUCH_LONG_PRESSED, }; ButtonThread(); int32_t runOnce() override; + void storeClickCount(); private: #ifdef BUTTON_PIN @@ -40,13 +41,16 @@ class ButtonThread : public concurrency::OSThread // set during IRQ static volatile ButtonEventType btnEvent; + // Store click count during callback, for later use + volatile int multipressClickCount = 0; + static void wakeOnIrq(int irq, int mode); // IRQ callbacks - static void touchPressed() { btnEvent = BUTTON_EVENT_TOUCH_PRESSED; } static void userButtonPressed() { btnEvent = BUTTON_EVENT_PRESSED; } static void userButtonDoublePressed() { btnEvent = BUTTON_EVENT_DOUBLE_PRESSED; } - static void userButtonMultiPressed() { btnEvent = BUTTON_EVENT_MULTI_PRESSED; } + static void userButtonMultiPressed(void *callerThread); // Retrieve click count from non-static Onebutton while still valid static void userButtonPressedLongStart(); static void userButtonPressedLongStop(); + static void touchPressedLongStart() { btnEvent = BUTTON_EVENT_TOUCH_LONG_PRESSED; } }; diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 8510562c4..11c0b7aa0 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1153,10 +1153,33 @@ void Screen::setup() MeshModule::observeUIEvents(&uiFrameEventObserver); } -void Screen::forceDisplay() +void Screen::forceDisplay(bool forceUiUpdate) { // Nasty hack to force epaper updates for 'key' frames. FIXME, cleanup. #ifdef USE_EINK + // If requested, make sure queued commands are run, and UI has rendered a new frame + if (forceUiUpdate) { + // No delay between UI frame rendering + setFastFramerate(); + + // Make sure all CMDs have run first + while (!cmdQueue.isEmpty()) + runOnce(); + + // Ensure at least one frame has drawn + uint64_t startUpdate; + do { + startUpdate = millis(); // Handle impossibly unlikely corner case of a millis() overflow.. + delay(10); + ui->update(); + } while (ui->getUiState()->lastUpdate < startUpdate); + + // Return to normal frame rate + targetFramerate = IDLE_FRAMERATE; + ui->setTargetFPS(targetFramerate); + } + + // Tell EInk class to update the display static_cast(dispdev)->forceDisplay(); #endif } diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index d03ba4320..2cb1cd5a9 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -20,7 +20,7 @@ class Screen void setOn(bool) {} void print(const char *) {} void doDeepSleep() {} - void forceDisplay() {} + void forceDisplay(bool forceUiUpdate = false) {} void startBluetoothPinScreen(uint32_t pin) {} void stopBluetoothPinScreen() {} void startRebootScreen() {} @@ -318,7 +318,7 @@ class Screen : public concurrency::OSThread int handleInputEvent(const InputEvent *arg); /// Used to force (super slow) eink displays to draw critical frames - void forceDisplay(); + void forceDisplay(bool forceUiUpdate = false); /// Draws our SSL cert screen during boot (called from WebServer) void setSSLFrames();