fix: wire through silent restart clear resume state with sdk (#2033)
## Summary After a silent reboot, there was a small window where the esp32 would listen for button presses but the full refresh would hold the event loop. This gave a UX experience where the silent reboot had completed to the home screen, a user taps select (at any time during the process), and they find themselves unexpectedly in a book. ## Additional Context This must land after https://github.com/crosspoint-reader/community-sdk/pull/11 and will need the submodule SHA changes included in. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? partially
This commit is contained in:
+11
-7
@@ -18,14 +18,18 @@ void HalDisplay::begin(bool seamless) {
|
||||
|
||||
einkDisplay.begin();
|
||||
|
||||
if (seamless) {
|
||||
// Defuse the SDK's X3 _x3InitialFullSyncsRemaining counter (no-op on X4)
|
||||
// so the first paint isn't promoted to FULL (~770ms). Skips the wakeup-
|
||||
// gated requestResync() below for the same reason.
|
||||
einkDisplay.skipInitialResync();
|
||||
return;
|
||||
}
|
||||
// Request resync after specific wakeup events to ensure clean display state.
|
||||
// Skip when seamless=true so the current screen content is preserved.
|
||||
if (!seamless) {
|
||||
const auto wakeupReason = gpio.getWakeupReason();
|
||||
if (wakeupReason == HalGPIO::WakeupReason::PowerButton || wakeupReason == HalGPIO::WakeupReason::AfterFlash ||
|
||||
wakeupReason == HalGPIO::WakeupReason::Other) {
|
||||
einkDisplay.requestResync();
|
||||
}
|
||||
const auto wakeupReason = gpio.getWakeupReason();
|
||||
if (wakeupReason == HalGPIO::WakeupReason::PowerButton || wakeupReason == HalGPIO::WakeupReason::AfterFlash ||
|
||||
wakeupReason == HalGPIO::WakeupReason::Other) {
|
||||
einkDisplay.requestResync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,12 @@ class HalDisplay {
|
||||
FAST_REFRESH // Fast refresh using custom LUT
|
||||
};
|
||||
|
||||
// Initialize the display hardware and driver
|
||||
// Pass seamless=true on any path where the panel already shows the
|
||||
// content it should after begin() returns (silent reboot's popup,
|
||||
// sleep-wake with a restored buffer). Skips the wakeup-gated
|
||||
// requestResync() and defuses the SDK's X3 _x3InitialFullSyncsRemaining
|
||||
// counter; otherwise the first two paints get promoted to FULL
|
||||
// (~770ms each on X3).
|
||||
void begin(bool seamless = false);
|
||||
|
||||
// Display dimensions
|
||||
|
||||
+1
-1
Submodule open-x4-sdk updated: 4d93e09f73...ff444b0f2c
+28
-4
@@ -146,6 +146,11 @@ void silentRestart() {
|
||||
silentRebootTarget = SILENT_REBOOT_TARGET_HOME;
|
||||
silentRebootMagic = SILENT_REBOOT_MAGIC;
|
||||
LOG_DBG("MAIN", "Silent restart (target=home)");
|
||||
// E-ink retains the previous frame until Home's first paint lands (~2-3s).
|
||||
// Without an overlay, users don't see the reboot and fire input through to
|
||||
// Home. Select on the default selectorIndex=0 then opens the most-recent
|
||||
// book, looking like a trampoline back to the reader they just exited.
|
||||
GUI.drawPopup(renderer, tr(STR_LOADING_POPUP));
|
||||
delay(50);
|
||||
ESP.restart();
|
||||
}
|
||||
@@ -154,6 +159,7 @@ void silentRestartToReader() {
|
||||
silentRebootTarget = SILENT_REBOOT_TARGET_READER;
|
||||
silentRebootMagic = SILENT_REBOOT_MAGIC;
|
||||
LOG_DBG("MAIN", "Silent restart (target=reader)");
|
||||
GUI.drawPopup(renderer, tr(STR_LOADING_POPUP));
|
||||
delay(50);
|
||||
ESP.restart();
|
||||
}
|
||||
@@ -336,7 +342,7 @@ void setup() {
|
||||
// We need 6 open files concurrently when parsing a new chapter
|
||||
if (!Storage.begin()) {
|
||||
LOG_ERR("MAIN", "SD card initialization failed");
|
||||
setupDisplayAndFonts();
|
||||
setupDisplayAndFonts(isSilentReboot);
|
||||
activityManager.goToFullScreenMessage("SD card error", EpdFontFamily::BOLD);
|
||||
return;
|
||||
}
|
||||
@@ -393,10 +399,11 @@ void setup() {
|
||||
// First serial output only here to avoid timing inconsistencies for power button press duration verification
|
||||
LOG_DBG("MAIN", "Starting CrossPoint version " CROSSPOINT_VERSION);
|
||||
|
||||
setupDisplayAndFonts(/*seamless=*/!APP_STATE.showBootScreen);
|
||||
setupDisplayAndFonts(isSilentReboot || /*seamless=*/!APP_STATE.showBootScreen);
|
||||
|
||||
// First paint after silent reboot is HALF_REFRESH (SDK forces it after begin()'s
|
||||
// panel reset); subsequent paints FAST.
|
||||
// Silent reboot suppresses the boot splash and the X3 initial-full-sync
|
||||
// arming (see HalDisplay::begin), so the first Home paint is FAST_REFRESH
|
||||
// (~500ms) and input dispatches against the visible menu.
|
||||
if (!isSilentReboot) {
|
||||
if (APP_STATE.showBootScreen) {
|
||||
activityManager.goToBoot();
|
||||
@@ -443,6 +450,23 @@ void setup() {
|
||||
activityManager.goToReader(path);
|
||||
}
|
||||
|
||||
if (isSilentReboot) {
|
||||
// Block until the first paint physically completes. refreshDisplay()
|
||||
// waits on the panel BUSY pin so when this returns the user can see the
|
||||
// new activity. Without the wait, an edge captured by gpio.update()
|
||||
// during boot dispatches against an invisible Home and the default
|
||||
// selectorIndex=0 opens the most-recent book.
|
||||
activityManager.requestUpdateAndWait();
|
||||
// Absorb any button held at this point into currentState as a non-edge:
|
||||
// two gpio.update() calls separated by > InputManager's 5ms debounce
|
||||
// transition the held bit through lastDebounceTime into currentState
|
||||
// without setting pressedEvents, so the first loop()'s own gpio.update()
|
||||
// sees state == currentState and emits nothing.
|
||||
gpio.update();
|
||||
delay(10);
|
||||
gpio.update();
|
||||
}
|
||||
|
||||
// Ensure we're not still holding the power button before leaving setup
|
||||
waitForPowerRelease();
|
||||
allowSleepAt = millis() + 2000;
|
||||
|
||||
Reference in New Issue
Block a user