Trackball click already wakes the screen and is the documented way to
revive the device. Firing it as an enter event after release means the
wake also confirms whatever was focused at the time. Gate short-click
emit on _clickFromScreenOn (already captured at click-down for the
long-press case) so the press only does its wake job when the screen
was off.
Trackball deltas crossing the nav threshold previously fired
up/down/left/right key events AND set _strongActivity=true, which
woke the screen from SCREEN_OFF on any pocket/backpack jostle and
also accumulated phantom navigation in the UI behind the dark screen.
Gate the nav-event block on isScreenOn(), mirroring the touch
suppression. Click/long-press detection stays unconditional so an
intentional trackball press still wakes the device.
The handler had no aspect filter, so it captured every announce from
every aspect of a peer (lxmf.delivery, lxmf.propagation, NomadNet
node, etc.). Each aspect has a different destination hash, so the
same Sideband/Columba peer appeared multiple times in the nodes list
— once with the display name (lxmf.delivery's app_data carries it)
and once or more as raw hex (other aspects had no/different name).
Restrict to lxmf.delivery: that's the only destination we ever route
LXMF messages to. Propagation/NomadNet visibility can come back later
behind an opt-in toggle if needed.
Note: existing saved contacts captured under other aspects will still
appear after this change since they're loaded from disk. They can be
removed manually via the contacts UI.
PATH_CONTACTS / PATH_MESSAGES / SD_PATH_CONTACTS / SD_PATH_MESSAGES
all ended in '/'. The FATFS layer used by the SD library and LittleFS
won't reliably enumerate when given a path ending in slash — opendir
returns a valid handle but readdir yields nothing. Direct file reads
(SD.open with FILE_READ for a known path) still work, which is why
settings/identity persisted but contacts and conversations vanished
on reboot.
Drop the trailing slash from the constants and add explicit "/" at
all concat sites: saveContact, removeContact, conversationDir,
sdConversationDir, and the migrateTruncatedDirs path builders.
Two pocket-carry quality-of-life additions:
1. Long-press (1.2s) trackball click blanks the screen if no LVGL
screen consumes the long-press. InputManager captures the power
state at click DOWN (_clickFromScreenOn) and only emits the
long-press if the screen was already on, so a long-press from
SCREEN_OFF wakes without immediately re-blanking.
2. Touch events are ignored entirely while screen is off, preventing
accidental wakes from pressure on the panel in a pocket/bag.
Trackball click and keyboard keys still wake normally.
InputManager now holds a Power* injected via setPowerMgr() in setup.
Sideband/Columba and other LXMF clients commonly include "/" in display
names (e.g. handle/path conventions). The allowlist in sanitizeName
stripped it, mangling "alice/columba" into "alicecolumba". Saved
contacts get re-sanitized on each load (line 393), so existing names
also lose the slash on next boot.
New public method that transitions to SCREEN_OFF on demand. A
_justWokeFromOff flag is set in activity() and cleared at the end
of loop() so a single keypress that woke the screen can't immediately
re-blank it via a hotkey/long-press handler running in the same tick.
Pure infrastructure — no UI binding included; consumers can wire it
to whatever input event they want (long-press, hotkey, etc.).
Python LXMF was sending bz2-compressed Resource transfers that
microReticulum cannot decode on ESP32 (bz2's working memory exceeds
available SRAM). The companion microReticulum commit (290a133)
removes bz2 entirely; this release advertises no-compression-support
in our LXMF announces so Python LXMF disables auto_compress when
sending to Ratdeck destinations.
Also pins lvgl and LovyanGFX minor versions in lib_deps to prevent
future fresh-clone builds from pulling incompatible 1.2.x / 8.4.x.
Thanks to @RFNexus for surfacing the ESP32 bz2 constraint.
encodeAnnounceName now emits msgpack fixarray(3):
[display_name(bin), stamp_cost=0, supported_functionality=[]]
Empty supported_functionality list = SF_COMPRESSION (=0x00) absent,
so Python LXMF disables auto_compress for our destinations and stops
shipping bz2-compressed Resources we can't decode. Always emit the
3-element form even when the name is empty — Python defaults to
auto_compress=True for legacy <3-element app_data.
Pairs with microReticulum 290a133 which removed bz2 entirely.
LovyanGFX 1.2.x bundles its own lvgl-compat headers that collide
with lvgl >=8.4 on shared types (lv_area_t, lv_font_t, etc.).
Tilde restricts fresh clones to the 1.1.x and 8.3.x ranges this
build was originally cached against, so `pio run` from a fresh
checkout doesn't randomly break.
CORE_DEBUG_LEVEL=0 drops Arduino core noise on expected-missing files, LittleFSFileSystem::open_file now actually works so path_table persists across reboots.
Link proof signature, TCP proof routing, and split-packet handling
were causing intermittent message failures with Python LXMF clients.
Route >254B messages through link delivery, add proof retry for LoRa.
Regenerated Montserrat 10/12/14 with Cyrillic and Latin Extended
Unicode ranges so messages in Russian, Ukrainian, Bulgarian, and
accented European languages render properly instead of rectangles.
Scroll: scrollToSelected() was defined but never called — LVGL didn't
know which pool row to show after viewport updates. Nodes at the
bottom of long lists were invisible. Now called after every sync.
Modal: Replace lv_obj_center() with explicit pixel positioning so
the action modal renders centered regardless of list scroll state.
Trackball: Click now sets character='\n' matching keyboard Enter for
complete event parity.