diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index e265cef4..7b4809f6 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -1946,7 +1946,7 @@ if (curr) curr->poll(); // Without this floor, changing readings (battery, uptime) trigger // back-to-back renders that cause continuous flashing. #ifdef EINK_FULL_REFRESH_ONLY - unsigned long minNext = millis() + 60000; // Full refresh: 60s idle (clock ticks per minute) + unsigned long minNext = millis() + 300000; // Full refresh: 5 min idle #else unsigned long minNext = millis() + 800; // Partial refresh: 800ms floor #endif @@ -2482,9 +2482,24 @@ void UITask::injectKey(char c) { if (_next_refresh < earliest) { _next_refresh = earliest; } + } +#ifdef EINK_FULL_REFRESH_ONLY + // Full-refresh displays (SSD1681): debounce printable character input. + // Compose typing (0x20-0x7E) pushes the render 2.5s into the future so + // the user can type a whole word before a ~2.2s full refresh fires. + // Navigation/special keys (arrows, enter, escape, etc.) refresh + // immediately so scrolling and screen changes remain responsive. + else if ((unsigned char)c >= 0x20 && (unsigned char)c <= 0x7E) { + unsigned long earliest = millis() + 2500; + if (_next_refresh < earliest) _next_refresh = earliest; } else { + _next_refresh = 100; // navigation key — refresh now + } +#else + else { _next_refresh = 100; // trigger refresh } +#endif } } diff --git a/src/helpers/ui/DisplayDriver.h b/src/helpers/ui/DisplayDriver.h index 9cae4e68..85793d38 100644 --- a/src/helpers/ui/DisplayDriver.h +++ b/src/helpers/ui/DisplayDriver.h @@ -48,21 +48,26 @@ public: } // Strip non-ASCII characters (emoji, etc.) from a UTF-8 string. - // Placeholder glyphs aren't safe across our fonts: GFXfonts (FreeSans) - // silently drop anything outside first..last, while the Adafruit built-in - // 5x7 font remaps c>=0xB0 by +1 so \xDB prints as ▄. Stripping the - // codepoint is the only behaviour that renders consistently across - // FastEPD (T5S3) and GxEPD (T-Deck Pro, T-Echo Lite, etc.) builds. + // Uses the same lead-byte-pattern approach as emojiDecodeUtf8() in + // EmojiSprites.h to determine sequence length, so both the channel + // message path (emojiSanitize) and the contact/advert name path + // consume identical byte spans for any given UTF-8 or malformed input. virtual void translateUTF8ToBlocks(char* dest, const char* src, size_t dest_size) { size_t j = 0; - for (size_t i = 0; src[i] != 0 && j < dest_size - 1; i++) { + size_t len = strlen(src); + for (size_t i = 0; i < len && j < dest_size - 1; ) { unsigned char c = (unsigned char)src[i]; - if (c >= 32 && c <= 126) { - dest[j++] = c; // ASCII printable - } else if (c >= 0x80) { - // Skip the whole UTF-8 codepoint (lead byte + continuation bytes) - while (src[i+1] && (src[i+1] & 0xC0) == 0x80) - i++; + if (c < 0x80) { + if (c >= 32) dest[j++] = c; // ASCII printable + i++; + } else { + // Determine sequence length from lead byte (matches emojiDecodeUtf8) + int skip; + if ((c & 0xE0) == 0xC0) skip = 2; + else if ((c & 0xF0) == 0xE0) skip = 3; + else if ((c & 0xF8) == 0xF0) skip = 4; + else skip = 1; // stray continuation or invalid + i += skip; } } dest[j] = 0;