From 503185c3c8fc0edb4b193ce4dd737575ba7bba7f Mon Sep 17 00:00:00 2001 From: Andrew Yong Date: Fri, 20 Mar 2026 12:32:13 +0800 Subject: [PATCH] feat: add ScreenSleepController and ScreenSleepObserver for display sleep management Extracts the display sleep/wake state machine from LGFXDriver::task_handler into a dedicated ScreenSleepController class, and adds a ScreenSleepObserver hook for peripheral backlight synchronisation (e.g. keyboard LED). Key design points: - Sinusoidal backlight fade (cos ease-out to sleep, sin ease-in to wake) replaces the previous linear step-per-tick approach. - Animation frames use setHardwareBrightness() so they never corrupt the wakeBrightness mirror; only the user-facing setBrightness() updates it. - A fresh dim clears wakeStartTime so a stale post-powersave wake animation cannot cause a brightness snap when activity later interrupts the dim. - The no-backlight activity-wake condition is guarded by !sleepRequested, matching the backlight path, so a manual sleep() request does not immediately re-wake on no-backlight devices. - Static requestWake/requestSleep/isScreenSleeping forwarders on DisplayDriver allow any driver context (e.g. I2C keyboard) to signal the controller without a direct pointer to LGFXDriver. - New virtual methods on DisplayDriver (panelSleep, panelWake, powerSaveOn, powerSaveOff, hasBacklight, getTouchIntPin, setHardwareBrightness) follow the established no-op default pattern so non-LGFX drivers are unaffected. - ScreenSleepObserver is an optional singleton; all call-sites null-guard so devices that don't need peripheral sync can leave it unregistered. docs/class-diagram.png needs updating (ScreenSleepController, ScreenSleepObserver). Signed-off-by: Andrew Yong --- include/graphics/driver/DisplayDriver.h | 21 +- include/graphics/driver/LGFXDriver.h | 125 +++------- .../graphics/driver/ScreenSleepController.h | 62 +++++ include/input/ScreenSleepObserver.h | 23 ++ source/graphics/driver/DisplayDriver.cpp | 2 + .../graphics/driver/ScreenSleepController.cpp | 228 ++++++++++++++++++ source/input/ScreenSleepObserver.cpp | 3 + 7 files changed, 371 insertions(+), 93 deletions(-) create mode 100644 include/graphics/driver/ScreenSleepController.h create mode 100644 include/input/ScreenSleepObserver.h create mode 100644 source/graphics/driver/ScreenSleepController.cpp create mode 100644 source/input/ScreenSleepObserver.cpp diff --git a/include/graphics/driver/DisplayDriver.h b/include/graphics/driver/DisplayDriver.h index 2a95d8cf..6fbccb53 100644 --- a/include/graphics/driver/DisplayDriver.h +++ b/include/graphics/driver/DisplayDriver.h @@ -2,6 +2,7 @@ #include "graphics/DeviceGUI.h" #include "graphics/LVGL/LVGLGraphics.h" +#include "graphics/driver/ScreenSleepController.h" #include #define H_NORM_PX(h_scr_percent) ((int16_t)((screenWidth / 100.0) * (h_scr_percent))) @@ -25,7 +26,17 @@ class DisplayDriver virtual ~DisplayDriver() {} virtual uint8_t getBrightness() { return 255; } - virtual void setBrightness(uint8_t timeout) {} + // setBrightness is the user-facing path: updates the wakeBrightness mirror in ScreenSleepController. + // setHardwareBrightness is the animation-internal path: drives hardware only, leaves the mirror untouched. + virtual void setBrightness(uint8_t brightness) {} + virtual void setHardwareBrightness(uint8_t brightness) {} + + virtual void panelSleep(void) {} + virtual void panelWake(void) {} + virtual void powerSaveOn(void) {} + virtual void powerSaveOff(void) {} + virtual bool hasBacklight(void) { return false; } + virtual int getTouchIntPin(void) { return -1; } virtual uint16_t getScreenTimeout() { return 0; } virtual void setScreenTimeout(uint16_t timeout) {} @@ -35,6 +46,13 @@ class DisplayDriver lv_display_t *getDisplay(void) { return display; } + ScreenSleepController *sleepController(void) { return _sleepController; } + + // signal wake/sleep from any context (e.g. I2C keyboard driver without GPIO interrupt) + static void requestWake(void) { if (_sleepController) _sleepController->wake(); } + static void requestSleep(void) { if (_sleepController) _sleepController->sleep(); } + static bool isScreenSleeping(void) { return _sleepController && _sleepController->isSleeping(); } + protected: LVGLGraphics lvgl; LVGLDisplay *display; @@ -42,4 +60,5 @@ class DisplayDriver DeviceGUI *view; uint16_t screenWidth; uint16_t screenHeight; + static ScreenSleepController *_sleepController; }; diff --git a/include/graphics/driver/LGFXDriver.h b/include/graphics/driver/LGFXDriver.h index 2d7a3094..f1335879 100644 --- a/include/graphics/driver/LGFXDriver.h +++ b/include/graphics/driver/LGFXDriver.h @@ -2,8 +2,10 @@ #include "LovyanGFX.h" #include "graphics/driver/DisplayDriverConfig.h" +#include "graphics/driver/ScreenSleepController.h" #include "graphics/driver/TFTDriver.h" #include "input/InputDriver.h" +#include "input/ScreenSleepObserver.h" #include "lvgl_private.h" #include "util/ILog.h" #include @@ -12,7 +14,7 @@ constexpr uint32_t defaultLongPressTime = 600; // ms until long press is detecte constexpr uint32_t defaultGestureLimit = 10; // x/y diff pixel until a swipe gesture is detected (lvgl default is 50) constexpr uint32_t defaultScreenTimeout = 30 * 1000; -constexpr uint32_t defaultBrightness = 153; +constexpr uint8_t defaultBrightness = 153; template class LGFXDriver : public TFTDriver { @@ -24,15 +26,23 @@ template class LGFXDriver : public TFTDriver bool hasTouch(void) override; bool hasButton(void) override { return lgfx->hasButton(); } bool hasLight(void) override { return lgfx->light(); } - bool isPowersaving(void) override { return powerSaving; } + bool isPowersaving(void) override { return _sleepController.isSleeping(); } void printConfig(void) override; void task_handler(void) override; - uint8_t getBrightness(void) override { return lgfx->getBrightness(); } - void setBrightness(uint8_t brightness) override; + uint8_t getBrightness(void) override { return lgfx->getBrightness(); } + void setHardwareBrightness(uint8_t brightness) override { lgfx->setBrightness(brightness); } + void setBrightness(uint8_t brightness) override; uint16_t getScreenTimeout() override { return screenTimeout / 1000; } - void setScreenTimeout(uint16_t timeout) override { screenTimeout = timeout * 1000; }; + void setScreenTimeout(uint16_t timeout) override { screenTimeout = (uint32_t)timeout * 1000; }; + + void panelSleep(void) override { lgfx->sleep(); } + void panelWake(void) override { lgfx->wakeup(); } + void powerSaveOn(void) override { lgfx->powerSaveOn(); } + void powerSaveOff(void) override { lgfx->powerSaveOff(); } + bool hasBacklight(void) override { return lgfx->light() != nullptr; } + int getTouchIntPin(void) override; protected: // lvgl callbacks have to be static cause it's a C library, not C++ @@ -41,13 +51,12 @@ template class LGFXDriver : public TFTDriver static void touchpad_read(lv_indev_t *indev_driver, lv_indev_data_t *data); uint32_t screenTimeout; - uint32_t lastBrightness; - bool powerSaving; private: void init_lgfx(void); static LGFX *lgfx; + ScreenSleepController _sleepController; size_t bufsize; lv_color_t *buf1; lv_color_t *buf2; @@ -59,7 +68,8 @@ template LGFX *LGFXDriver::lgfx = nullptr; template LGFXDriver::LGFXDriver(uint16_t width, uint16_t height) : TFTDriver(lgfx ? lgfx : new LGFX, width, height), screenTimeout(defaultScreenTimeout), - lastBrightness(defaultBrightness), powerSaving(false), bufsize(0), buf1(nullptr), buf2(nullptr), calibrating(false) + _sleepController(this, defaultBrightness), + bufsize(0), buf1(nullptr), buf2(nullptr), calibrating(false) { lgfx = this->tft; } @@ -67,7 +77,8 @@ LGFXDriver::LGFXDriver(uint16_t width, uint16_t height) template LGFXDriver::LGFXDriver(const DisplayDriverConfig &cfg) : TFTDriver(lgfx ? lgfx : new LGFX(cfg), cfg.width(), cfg.height()), screenTimeout(defaultScreenTimeout), - lastBrightness(defaultBrightness), powerSaving(false), bufsize(0), buf1(nullptr), buf2(nullptr), calibrating(false) + _sleepController(this, defaultBrightness), + bufsize(0), buf1(nullptr), buf2(nullptr), calibrating(false) { lgfx = this->tft; } @@ -81,91 +92,18 @@ template bool LGFXDriver::hasTouch(void) #endif } -template void LGFXDriver::task_handler(void) +template int LGFXDriver::getTouchIntPin(void) { - // handle display timeout - if ((screenTimeout > 0 && lv_display_get_inactive_time(NULL) > screenTimeout) || powerSaving || - (DisplayDriver::view->isScreenLocked())) { - // sleep screen only if there are means for wakeup - if (DisplayDriver::view->getInputDriver()->hasPointerDevice() || hasTouch() || - DisplayDriver::view->getInputDriver()->hasKeyboardDevice() || hasButton()) { - if (hasLight()) { - if (!powerSaving) { - // dim display brightness slowly down - uint32_t brightness = lgfx->getBrightness(); - if (brightness > 0) { - lgfx->setBrightness(brightness - 1); - } else { - ILOG_INFO("enter powersave"); - DisplayDriver::view->screenSaving(true); - if (hasTouch() && hasButton()) { - ILOG_DEBUG("disable touch, enable button input"); - lv_indev_enable(DisplayDriver::touch, false); - lv_indev_enable(InputDriver::instance()->getButton(), true); - } - lgfx->sleep(); - lgfx->powerSaveOn(); - powerSaving = true; - } - } - if (powerSaving) { - int pin_int = -1; - if (hasTouch()) { #ifndef CUSTOM_TOUCH_DRIVER - pin_int = lgfx->touch()->config().pin_int; + return lgfx->touch() ? lgfx->touch()->config().pin_int : -1; #else - pin_int = lgfx->getTouchInt(); -#endif - } - if (hasButton()) { -#ifdef BUTTON_PIN // only relevant for CYD scenario - pin_int = BUTTON_PIN; + return lgfx->getTouchInt(); #endif - } - if ((pin_int >= 0 && DisplayDriver::view->sleep(pin_int)) || - (screenTimeout + 50 > lv_display_get_inactive_time(NULL) && !DisplayDriver::view->isScreenLocked())) { - delay(2); // let the CPU finish to restore all register in case of light sleep - // woke up by touch or button - ILOG_INFO("leaving powersave"); - powerSaving = false; - DisplayDriver::view->triggerHeartbeat(); - lgfx->powerSaveOff(); - lgfx->wakeup(); - lgfx->setBrightness(lastBrightness); - DisplayDriver::view->screenSaving(false); - if (hasTouch() && hasButton()) { - ILOG_DEBUG("enable touch, disable button input"); - lv_indev_enable(DisplayDriver::touch, true); - lv_indev_enable(InputDriver::instance()->getButton(), false); - } - lv_display_trigger_activity(NULL); - } else { - // we woke up due to e.g. serial traffic (or sleep() simply not implemented) - // continue with processing loop and enter sleep() again next round - } - } - } - // no BL pin defined to control brightness, so show blank screen instead - else { - if (!powerSaving) { - DisplayDriver::view->blankScreen(true); - lgfx->sleep(); - lgfx->powerSaveOn(); - powerSaving = true; - } - if (screenTimeout > lv_display_get_inactive_time(NULL)) { - DisplayDriver::view->blankScreen(false); - lgfx->powerSaveOff(); - lgfx->wakeup(); - powerSaving = false; - lv_disp_trig_activity(NULL); - } - } - } - } else if (lgfx->getBrightness() < lastBrightness) { - lgfx->setBrightness(lastBrightness); - lastBrightness = lgfx->getBrightness(); - } +} + +template void LGFXDriver::task_handler(void) +{ + _sleepController.tick(screenTimeout, hasTouch(), hasButton()); if (!calibrating) { DisplayDriver::task_handler(); @@ -337,6 +275,9 @@ template void LGFXDriver::init(DeviceGUI *gui) lv_timer_set_period(timer, 10); // 100Hz as I2C touch controllers support #endif } + + _sleepController.setContext(DisplayDriver::view, DisplayDriver::touch); + DisplayDriver::_sleepController = &_sleepController; } template void LGFXDriver::init_lgfx(void) @@ -413,8 +354,8 @@ template bool LGFXDriver::calibrate(uint16_t parameters[8]) template void LGFXDriver::setBrightness(uint8_t brightness) { - lgfx->setBrightness(brightness); - lastBrightness = brightness; + setHardwareBrightness(brightness); + _sleepController.setWakeBrightness(brightness); } template void LGFXDriver::printConfig(void) diff --git a/include/graphics/driver/ScreenSleepController.h b/include/graphics/driver/ScreenSleepController.h new file mode 100644 index 00000000..ba8a3d37 --- /dev/null +++ b/include/graphics/driver/ScreenSleepController.h @@ -0,0 +1,62 @@ +#pragma once + +#include "lvgl.h" +#include + +class DisplayDriver; +class DeviceGUI; + +/** + * @brief Controls the display sleep/wake state machine and sinusoidal backlight + * fade animations. Owned by LGFXDriver; accessible externally via + * DisplayDriver::sleepController(). + * + * Unlike a traditional screensaver (which keeps the screen on to prevent + * burn-in), this class powers the display off entirely and coordinates + * peripheral backlight synchronisation via ScreenSleepObserver. + */ +class ScreenSleepController +{ + public: + explicit ScreenSleepController(DisplayDriver *panel, uint8_t wakeBrightness); + void setContext(DeviceGUI *view, lv_indev_t *touch); + + void wake(void); // request wake — safe to call from any context + void sleep(void); // request sleep — safe to call from any context + bool isSleeping(void) const; + + void tick(uint32_t screenTimeout, bool hasTouch, bool hasButton); + + void setWakeBrightness(uint8_t brightness); + uint8_t getWakeBrightness(void) const; + + private: + void dimStep(bool hasTouch, bool hasButton); + void wakeStep(void); + void enterPowerSave(bool hasTouch, bool hasButton); + void exitPowerSave(bool hasTouch, bool hasButton); + void checkWakeConditions(uint32_t screenTimeout, bool hasTouch, bool hasButton); + void startWakeFromCurrent(uint8_t targetBrightness); + + // ms for the brightness fade triggered by a manual sleep request or screen-lock + static constexpr uint16_t defaultDimDuration = 1000; + // ms for the brightness fade triggered by the inactivity timeout + static constexpr uint16_t timeoutDimDuration = 10000; + + DisplayDriver *panel; + DeviceGUI *view = nullptr; + lv_indev_t *touch = nullptr; + + volatile bool wakeRequested = false; + volatile bool sleepRequested = false; + bool powerSaving = false; + + // Target brightness to restore after waking. Mirrored here on every user-facing + // setBrightness() call; animation frames use setHardwareBrightness() so they never + // corrupt this value. + uint8_t wakeBrightness = 0; + uint32_t dimStartTime = 0; + uint8_t dimStartBrightness = 0; + uint16_t dimDuration = 0; + uint32_t wakeStartTime = 0; +}; diff --git a/include/input/ScreenSleepObserver.h b/include/input/ScreenSleepObserver.h new file mode 100644 index 00000000..0d112a66 --- /dev/null +++ b/include/input/ScreenSleepObserver.h @@ -0,0 +1,23 @@ +#pragma once + +/** + * @brief Optional observer that synchronizes peripheral backlights (e.g. a keyboard + * LED) with the display fade animation. Register via setInstance(); all + * call-sites null-guard, so it is safe to leave unset on devices that don't need it. + */ +class ScreenSleepObserver +{ + public: + static ScreenSleepObserver *instance(void) { return observer; } + static void setInstance(ScreenSleepObserver *obs) { observer = obs; } + + virtual void onScreenSleep(void) {} + // progress: animation progress 0.0–1.0 (0 = animation start, 1 = animation complete); + // fadingIn: true = waking (0→full brightness), false = dimming (full brightness→0) + virtual void applyBrightnessProgress(float progress, bool fadingIn) {} + + virtual ~ScreenSleepObserver() = default; + + protected: + static ScreenSleepObserver *observer; +}; diff --git a/source/graphics/driver/DisplayDriver.cpp b/source/graphics/driver/DisplayDriver.cpp index 204763e8..940854b0 100644 --- a/source/graphics/driver/DisplayDriver.cpp +++ b/source/graphics/driver/DisplayDriver.cpp @@ -1,6 +1,8 @@ #include "graphics/driver/DisplayDriver.h" #include "util/ILog.h" +ScreenSleepController *DisplayDriver::_sleepController = nullptr; + #if LV_USE_PROFILER #if defined(ARCH_PORTDUINO) #include diff --git a/source/graphics/driver/ScreenSleepController.cpp b/source/graphics/driver/ScreenSleepController.cpp new file mode 100644 index 00000000..6763415a --- /dev/null +++ b/source/graphics/driver/ScreenSleepController.cpp @@ -0,0 +1,228 @@ +#include "graphics/driver/ScreenSleepController.h" + +#include "graphics/DeviceGUI.h" +#include "graphics/driver/DisplayDriver.h" +#include "input/InputDriver.h" +#include "input/ScreenSleepObserver.h" +#include "util/ILog.h" +#include "lvgl.h" +#include + +ScreenSleepController::ScreenSleepController(DisplayDriver *panel, uint8_t wakeBrightness) + : panel(panel), wakeBrightness(wakeBrightness) +{ +} + +void ScreenSleepController::setContext(DeviceGUI *v, lv_indev_t *t) +{ + view = v; + touch = t; +} + +void ScreenSleepController::wake(void) +{ + wakeRequested = true; +} + +void ScreenSleepController::sleep(void) +{ + sleepRequested = true; +} + +bool ScreenSleepController::isSleeping(void) const +{ + return powerSaving; +} + +void ScreenSleepController::setWakeBrightness(uint8_t brightness) +{ + wakeBrightness = brightness; +} + +uint8_t ScreenSleepController::getWakeBrightness(void) const +{ + return wakeBrightness; +} + +void ScreenSleepController::tick(uint32_t screenTimeout, bool hasTouch, bool hasButton) +{ + bool shouldSleep = (screenTimeout > 0 && lv_display_get_inactive_time(NULL) > screenTimeout) || powerSaving || + (view && view->isScreenLocked()) || sleepRequested; + + if (shouldSleep) { + // Only sleep if there is a means to wake up again + if ((view && (view->getInputDriver()->hasPointerDevice() || view->getInputDriver()->hasKeyboardDevice())) || + hasTouch || hasButton) { + if (panel->hasBacklight()) { + if (!powerSaving) dimStep(hasTouch, hasButton); + if (powerSaving) checkWakeConditions(screenTimeout, hasTouch, hasButton); + } else { + // No backlight: blank/unblank directly without a brightness fade + if (!powerSaving) { + view->blankScreen(true); + if (auto *id = InputDriver::instance()) { + if (id->hasEncoderDevice()) + lv_indev_enable(id->getEncoder(), false); + } + panel->panelSleep(); + panel->powerSaveOn(); + powerSaving = true; + } + if ((screenTimeout + 50 > lv_display_get_inactive_time(NULL) && !view->isScreenLocked() && !sleepRequested) || + wakeRequested) { + view->blankScreen(false); + panel->powerSaveOff(); + panel->panelWake(); + if (auto *id = InputDriver::instance()) { + if (id->hasEncoderDevice()) + lv_indev_enable(id->getEncoder(), true); + } + powerSaving = false; + wakeRequested = false; + sleepRequested = false; + lv_display_trigger_activity(NULL); + } + } + } + } else if (panel->getBrightness() < wakeBrightness) { + // Brightness is below target — either a timeout fade was interrupted by activity, + // or we just exited powersave and the wake animation is in progress. + if (wakeStartTime == 0) + startWakeFromCurrent(wakeBrightness); + wakeStep(); + } +} + +// Join the wake sinusoid at the current brightness so the animation reverses smoothly +// from wherever the dim-to-off curve was cancelled. +// asinf(current/targetBrightness) gives the sine-curve phase that matches the current brightness, +// which is used to backdate wakeStartTime so wakeStep() picks up at the right point. +void ScreenSleepController::startWakeFromCurrent(uint8_t targetBrightness) +{ + float frac = targetBrightness > 0 ? (float)panel->getBrightness() / (float)targetBrightness : 0.0f; + if (frac > 1.0f) frac = 1.0f; + wakeStartTime = millis() - (uint32_t)(asinf(frac) / 1.5707963f * defaultDimDuration); + dimStartTime = 0; +} + +// Advance one frame of the dim-to-off animation, or enter powersave when complete. +// Handles: starting a fresh dim, speeding up via manual sleep request, and +// cancelling mid-fade when a wake key is pressed. +void ScreenSleepController::dimStep(bool hasTouch, bool hasButton) +{ + uint8_t brightness = panel->getBrightness(); + if (brightness > 0) { + if (dimStartTime == 0) { + dimStartTime = millis(); + dimStartBrightness = brightness; + wakeStartTime = 0; // discard any stale wake animation from a prior cycle + dimDuration = (sleepRequested || (view && view->isScreenLocked())) + ? defaultDimDuration : timeoutDimDuration; + wakeRequested = false; // discard any stale wake flag from before the dim started + if (auto *obs = ScreenSleepObserver::instance()) obs->onScreenSleep(); + } + if (wakeRequested) { + // Cancel mid-fade: reverse the animation smoothly from current brightness. + startWakeFromCurrent(dimStartBrightness); + wakeRequested = false; + sleepRequested = false; + lv_display_trigger_activity(NULL); + } else { + // Sinusoidal ease-out (cos t·π/2, peak→0) + float progress = (float)(millis() - dimStartTime) / (float)dimDuration; + if (progress > 1.0f) progress = 1.0f; + panel->setHardwareBrightness((uint8_t)(dimStartBrightness * cosf(progress * 1.5707963f))); + if (auto *obs = ScreenSleepObserver::instance()) obs->applyBrightnessProgress(progress, false); + } + } else { + enterPowerSave(hasTouch, hasButton); + } +} + +// Advance one frame of the off-to-bright wake animation. +// Sinusoidal ease-in (sin t·π/2, 0→peak) mirrors the dim curve. +// wakeStartTime may be backdated (via asinf) to join the curve mid-way when +// cancelling a dim or waking from a partially-off state. +void ScreenSleepController::wakeStep(void) +{ + dimStartTime = 0; + if (wakeStartTime == 0) { + panel->setHardwareBrightness(wakeBrightness); + if (auto *obs = ScreenSleepObserver::instance()) obs->applyBrightnessProgress(1.0f, true); + return; + } + float progress = (float)(millis() - wakeStartTime) / (float)defaultDimDuration; + if (progress >= 1.0f) { + panel->setHardwareBrightness(wakeBrightness); + wakeStartTime = 0; + if (auto *obs = ScreenSleepObserver::instance()) obs->applyBrightnessProgress(1.0f, true); + } else { + panel->setHardwareBrightness((uint8_t)(wakeBrightness * sinf(progress * 1.5707963f))); + if (auto *obs = ScreenSleepObserver::instance()) obs->applyBrightnessProgress(progress, true); + } +} + +// Power down the panel and disable indevs that should not fire during sleep. +void ScreenSleepController::enterPowerSave(bool hasTouch, bool hasButton) +{ + ILOG_INFO("enter powersave"); + view->screenSaving(true); + if (auto *id = InputDriver::instance()) { + if (hasTouch && hasButton) { + ILOG_DEBUG("disable touch, enable button input"); + lv_indev_enable(touch, false); + lv_indev_enable(id->getButton(), true); + } + if (id->hasEncoderDevice()) + lv_indev_enable(id->getEncoder(), false); + } + panel->panelSleep(); + panel->powerSaveOn(); + powerSaving = true; + if (auto *obs = ScreenSleepObserver::instance()) obs->applyBrightnessProgress(1.0f, false); // dim animation complete: peripheral backlight off +} + +// Check whether any wake condition is met and, if so, power the panel back up. +// Manual-sleep guard: the activity-based condition is suppressed while sleepRequested +// is true, preventing an immediate re-wake when the user was recently active. +void ScreenSleepController::checkWakeConditions(uint32_t screenTimeout, bool hasTouch, bool hasButton) +{ + int pin_int = panel->getTouchIntPin(); + if (hasButton) { +#ifdef BUTTON_PIN // only relevant for CYD scenario + pin_int = BUTTON_PIN; +#endif + } + bool activityWake = !sleepRequested && + screenTimeout + 50 > lv_display_get_inactive_time(NULL) && + !(view && view->isScreenLocked()); + if ((pin_int >= 0 && view->sleep(pin_int)) || activityWake || wakeRequested) + exitPowerSave(hasTouch, hasButton); +} + +// Power the panel back up, re-enable indevs, and start the wake brightness animation. +void ScreenSleepController::exitPowerSave(bool hasTouch, bool hasButton) +{ + delay(2); // let the CPU finish restoring registers in case of light sleep + ILOG_INFO("leaving powersave"); + powerSaving = false; + dimStartTime = 0; + wakeRequested = false; + sleepRequested = false; + view->triggerHeartbeat(); + panel->powerSaveOff(); + panel->panelWake(); + panel->setHardwareBrightness(0); + wakeStartTime = millis(); + view->screenSaving(false); + if (auto *id = InputDriver::instance()) { + if (hasTouch && hasButton) { + ILOG_DEBUG("enable touch, disable button input"); + lv_indev_enable(touch, true); + lv_indev_enable(id->getButton(), false); + } + if (id->hasEncoderDevice()) + lv_indev_enable(id->getEncoder(), true); + } + lv_display_trigger_activity(NULL); +} diff --git a/source/input/ScreenSleepObserver.cpp b/source/input/ScreenSleepObserver.cpp new file mode 100644 index 00000000..43a73f6f --- /dev/null +++ b/source/input/ScreenSleepObserver.cpp @@ -0,0 +1,3 @@ +#include "input/ScreenSleepObserver.h" + +ScreenSleepObserver *ScreenSleepObserver::observer = nullptr;