diff --git a/src/ui-touch/ConsoleUI.cpp b/src/ui-touch/ConsoleUI.cpp index 0ef7360..99e89ab 100644 --- a/src/ui-touch/ConsoleUI.cpp +++ b/src/ui-touch/ConsoleUI.cpp @@ -50,6 +50,9 @@ extern int luaHostDiscoverAt(int idx, char* pk_hex, size_t pk_cap, char* name, // Board touch driver, read directly. lvglTouchRead() is only an adapter that // feeds LVGL, so there is nothing to unpick here: the driver polls either way. extern bool heltecV4CapTouchGetLive(uint16_t* x, uint16_t* y); +#if defined(ESP32) +extern void consoleHostRebootToUi(); // clears the pref, flushes it, reboots +#endif #endif namespace { @@ -526,11 +529,13 @@ void submit() { // The way back to the graphical UI. One of three, per CONSOLE_MODE.md: this, // a key held at boot, and clearing the pref over serial or the flasher. if (!strcasecmp(cmd, "ui") || !strcasecmp(cmd, "exit")) { - touchPrefsSetConsoleMode(false); consoleWriteLine("switching to the graphical UI, rebooting..."); render(); delay(600); // let the line land on the panel before the reset - ESP.restart(); + // Clears the pref AND flushes it. Doing the write here and calling + // ESP.restart() left the queued snapshot unwritten, so this came straight + // back to the console. + consoleHostRebootToUi(); return; } #endif diff --git a/src/ui-touch/UITask.cpp b/src/ui-touch/UITask.cpp index 0b056fb..8aca006 100644 --- a/src/ui-touch/UITask.cpp +++ b/src/ui-touch/UITask.cpp @@ -51966,8 +51966,12 @@ void UITask::rebootDevice() { char why[64]; chatSaveFailText(why, sizeof why); char msg[128]; snprintf(msg, sizeof msg, "%s\n%s", TR("Chat history save FAILED"), why); - showAlert(msg, 1800); + showAlert(msg, 1800); // console-aware: prints instead when there is no LVGL +#if CAP_CONSOLE + if (!s_console_mode) lv_refr_now(NULL); +#else lv_refr_now(NULL); +#endif delay(1500); // let the warning actually paint before the reset } discoveredFlushNow(); // persist the Discovered ring before we go down @@ -51977,6 +51981,27 @@ void UITask::rebootDevice() { if (_board) _board->reboot(); } +#if CAP_CONSOLE && defined(ESP32) +// The console's way back to the graphical UI (the `ui` / `exit` command). +// +// It must go through rebootDevice(), not ESP.restart(), and the reason is not +// obvious: touchPrefsSetConsoleMode() only QUEUES an A/B snapshot. SdNvsPrefs +// deliberately keeps filesystem I/O off the calling thread, and +// touchPrefsFlush() -- which rebootDevice() calls last -- is what forces the +// queued write out. Restarting directly discarded it, so the pref still said +// "console" on the next boot and `ui` looked like it did nothing. +// +// Going through rebootDevice() also means the console exits on the same terms +// as everything else: chat history, the Discovered ring and the sync replay +// ring are all persisted first. +void consoleHostRebootToUi() { + touchPrefsSetConsoleMode(false); + if (g_lv.task) { g_lv.task->rebootDevice(); return; } + touchPrefsFlush(); // no UITask (should not happen): at least do not lose the pref + ESP.restart(); +} +#endif + void UITask::msgRead(int msgcount) { _msgcount = msgcount; } // Shared core for newMsg / newMsgFromPubWithMeta. Bundles the channel-vs-DM