revert(eink): drop full refresh on screen change — too much black-flash

A full (non-partial) refresh on every screen change (638eea7b) turned out to be
far too aggressive on real e-ink hardware: every navigation black-flashes,
which is worse than the ghosting it was clearing. Remove the whole mechanism —
DisplayDriver::forceFullRefresh() virtual, GxEPDDisplay's _force_full flag and
override, the endFrame() branch, and the setCurrScreen() call. E-ink is back to
interval-only full refreshes (Settings > Full refresh interval).

The favourites "(gone)"-tile prune that shipped in the same commit is kept.

Builds green: WioTrackerL1Eink_companion_solo_dual.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-07-06 20:58:51 +02:00
co-authored by Claude Opus 4.8
parent 51a0c46828
commit 8d8eace99c
5 changed files with 1 additions and 13 deletions
@@ -1923,10 +1923,6 @@ void UITask::setCurrScreen(UIScreen* c) {
// that mistake is an inert no-op instead of a null deref in render()/poll(). // that mistake is an inert no-op instead of a null deref in render()/poll().
if (!c) return; if (!c) return;
curr = c; curr = c;
// E-ink: force a full refresh on the first frame of the new screen so leftover
// ghosting from the previous screen is cleared (the N-partials interval alone
// doesn't catch navigation). No-op on OLED.
if (_display) _display->forceFullRefresh();
c->onShow(); // central per-visit reset hook (see UIScreen::onShow) c->onShow(); // central per-visit reset hook (see UIScreen::onShow)
_next_refresh = 100; _next_refresh = 100;
} }
-1
View File
@@ -26,7 +26,6 @@
### Under the hood ### Under the hood
- **OLED draws less** — the SH1106 driver now skips pushing a frame over I²C when it's byte-identical to the last one, cutting redundant traffic and a little power on the static screens (clock, home) that don't change between updates. - **OLED draws less** — the SH1106 driver now skips pushing a frame over I²C when it's byte-identical to the last one, cutting redundant traffic and a little power on the static screens (clock, home) that don't change between updates.
- **e-ink: cleaner screen changes** — switching screens now forces a full (non-partial) refresh on the first frame of the new screen, clearing leftover ghosting from the previous screen that the periodic partial-refresh interval didn't catch.
## MeshCore Solo Companion Firmware v1.21 ## MeshCore Solo Companion Firmware v1.21
-1
View File
@@ -272,7 +272,6 @@ public:
virtual void setBrightness(uint8_t level) { } // level 0-4 (min to max), no-op default virtual void setBrightness(uint8_t level) { } // level 0-4 (min to max), no-op default
virtual void setDisplayRotation(uint8_t rot) { } // 0-3, no-op for fixed-orientation displays virtual void setDisplayRotation(uint8_t rot) { } // 0-3, no-op for fixed-orientation displays
virtual void setFullRefreshInterval(uint8_t n) { } // e-ink: do full refresh every n partial refreshes (0=never) virtual void setFullRefreshInterval(uint8_t n) { } // e-ink: do full refresh every n partial refreshes (0=never)
virtual void forceFullRefresh() { } // e-ink: make the next frame a full (non-partial) refresh — clears ghosting on screen change; no-op on OLED
virtual void endFrame() = 0; virtual void endFrame() = 0;
#ifdef ENABLE_SCREENSHOT #ifdef ENABLE_SCREENSHOT
+1 -5
View File
@@ -275,14 +275,10 @@ void GxEPDDisplay::endFrame() {
uint32_t crc = display_crc.finalize(); uint32_t crc = display_crc.finalize();
if (crc != last_display_crc_value) { if (crc != last_display_crc_value) {
bool partial = true; bool partial = true;
// A forced full refresh (e.g. screen change) clears inter-screen ghosting. if (_full_refresh_interval > 0 && ++_partial_count >= _full_refresh_interval) {
// Short-circuit so _partial_count isn't bumped on the forced pass; the block
// resets it either way, matching the interval-driven full refresh.
if (_force_full || (_full_refresh_interval > 0 && ++_partial_count >= _full_refresh_interval)) {
partial = false; partial = false;
_partial_count = 0; _partial_count = 0;
} }
_force_full = false;
display.display(partial); display.display(partial);
last_display_crc_value = crc; last_display_crc_value = crc;
} }
-2
View File
@@ -64,7 +64,6 @@ class GxEPDDisplay : public DisplayDriver {
int _text_sz = 1; int _text_sz = 1;
uint8_t _full_refresh_interval = 0; uint8_t _full_refresh_interval = 0;
uint8_t _partial_count = 0; uint8_t _partial_count = 0;
bool _force_full = false; // set by forceFullRefresh(); consumed by the next endFrame()
int16_t drawLemonChar(int16_t x, int16_t y, uint32_t cp, int sc); int16_t drawLemonChar(int16_t x, int16_t y, uint32_t cp, int sc);
uint8_t lemonXAdvance(uint32_t cp, int sc); uint8_t lemonXAdvance(uint32_t cp, int sc);
@@ -145,6 +144,5 @@ public:
} }
void setDisplayRotation(uint8_t rot) override; void setDisplayRotation(uint8_t rot) override;
void setFullRefreshInterval(uint8_t n) override { _full_refresh_interval = n; _partial_count = 0; } void setFullRefreshInterval(uint8_t n) override { _full_refresh_interval = n; _partial_count = 0; }
void forceFullRefresh() override { _force_full = true; }
void endFrame() override; void endFrame() override;
}; };