Skip to content

fix(lvgl): stand the displays down while the app is suspended - #53

Merged
genmon merged 1 commit into
mainfrom
genmon/lvgl-stands-down-while-suspended
Sep 22, 2026
Merged

genmon merged 1 commit into
mainfrom
genmon/lvgl-stands-down-while-suspended

Conversation

@genmon

@genmon genmon commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

An overlay claim on a dual-role surface left its own last frame on the glass after it released, under a live app.

What happens

A board suppresses its blit door for the duration of a claim — every board in the fleet with a dual-role surface does this, so the overlay cannot be painted over a frame later. But suppression drops the pixels while LVGL goes on rendering and goes on getting its lv_display_flush_ready. The library believed those areas had reached the panel, so when the claim lifted nothing was dirty: the overlay's frame sat there with only the widgets that happened to change painting over it.

A body that mutates on change rather than rebuilding — the idiom the authoring sheets teach — never repaints the rest at all.

The fix

LvglModule stands its displays down while the app is suspended (a suspended app cannot draw anything new, so nothing is lost) and stands them up on resume, which invalidates each active screen and repaints every pixel the overlay owned. Only for targets this module still owns: before an app's first bind there is nothing to repaint, and a target lgfx claimed is not ours to touch.

To hear about the suspension at all, onAppRunning moves from Driver up to Extension, and Sandbox::notifyAppRunning calls it on every declared extension rather than only the Drivers among them. That is the right home for it: the extension that most needs to know the app stopped is a graphics module, which owns no hardware and so was never a Driver. Existing Driver overrides are untouched — same signature, same slot, so this is source-compatible.

Evidence

Found on an oracle (360×360 round face, dual-role: cfg.systemDisplay and an app surface) and reproduced in that board's wasm sim, where a still app left 129600 of 129600 pixels unrepainted after an agent_status claim released, and 0 with this change. The consumer repo now smokes that on every board with a status surface.

  • test_overlay gains one case: a module-less Extension hears the overlay suspension.
  • 225 unit tests pass; cppcheck clean.

Version

0.8.6-dev, with docs/changelog.md and docs/api.md updated (the Extension interface table, the Driver "added interface" section, and the suspendApp paragraph, which said drivers receive the hook).

🤖 Generated with Claude Code

An overlay claim on a dual-role surface left its own last frame on the glass
after it released, under a live app.

The board suppresses its blit door for the duration of a claim (every board in
the fleet with a dual-role surface does this) — but suppression drops the
PIXELS while LVGL goes on rendering and goes on getting its
lv_display_flush_ready. The library believed those areas had reached the
panel, so when the claim lifted nothing was dirty: the overlay's frame sat
there with only the widgets that happened to change painting over it. A device
whose app mutates on change — which is the idiom the authoring sheets teach —
never repaints the rest at all.

So LvglModule now stands its displays down while the app is suspended (the app
cannot draw anything new while suspended, so nothing is lost) and stands them
up on resume, which invalidates each active screen and repaints every pixel
the overlay owned. Only for targets this module still owns: before an app's
first bind there is nothing to repaint, and a target lgfx claimed is not ours.

To hear about the suspension at all, onAppRunning moves from Driver up to
Extension, and Sandbox::notifyAppRunning calls it on every declared extension
rather than only the Drivers among them. That is the right home for it: the
extension that most needs to know the app stopped is a graphics module, which
owns no hardware and so was never a Driver. Existing Driver overrides are
untouched — same signature, same slot.

Found on an oracle (360x360 round face, dual-role) and reproduced in that
board's wasm sim, where a still app left 129600 of 129600 pixels unrepainted
after an agent_status claim released.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@genmon
genmon merged commit 2551753 into main Sep 22, 2026
4 checks passed
@genmon
genmon deleted the genmon/lvgl-stands-down-while-suspended branch September 22, 2026 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant