Skip to content

system/nxstore: add LVGL touchscreen app-store frontend#3643

Draft
aviralgarg05 wants to merge 2 commits into
apache:masterfrom
aviralgarg05:gsoc/nxstore-app-store-ui-pr6
Draft

system/nxstore: add LVGL touchscreen app-store frontend#3643
aviralgarg05 wants to merge 2 commits into
apache:masterfrom
aviralgarg05:gsoc/nxstore-app-store-ui-pr6

Conversation

@aviralgarg05

Copy link
Copy Markdown
Contributor

Note: Please adhere to Contributing Guidelines.

Summary

This is another slice of the ongoing GSoC 2026 Dynamic ELF loading and
nxpkg work for NuttX.

An earlier draft PR (#3474) carries the initial nxpkg package
lifecycle helper. PR #3642 extends nxpkg with network sync, install
hardening, and the rest of its CLI (update / remove / rollback /
available). This PR adds the first graphical frontend on top of that:
system/nxstore, an LVGL touchscreen app store.

Series order for this PR:

Concretely, nxstore is:

  • a card-based app list (populate_app_list()): each row shows name,
    version, and description/status, install/launch state reflected via
    icon glyph and color, and a sliding-segment progress bar during
    install (no real byte-level progress is available from
    pkg_install(), so this reads as "actively working" without
    fabricating a percentage)
  • a supervisor screen (build_run_screen() /
    nxstore_enter_running_screen()): a launched app gets a dedicated
    screen with a name label and a Close button, confined to a border
    region the launched app's own framebuffer output is expected to never
    draw into, so switching back to it doesn't fight over pixels with
    whatever the app already put in the framebuffer
  • close/reap handling (close_running_app_event_cb() /
    nxstore_poll_running_app()): sends SIGTERM and polls
    waitpid(WNOHANG), explicitly treating ECHILD as "already gone"
    rather than "still running" (the close-button handler and the passive
    per-loop poll both race to reap the same child). There is
    deliberately no generic force-kill fallback: an earlier version of
    this code called task_delete() when SIGTERM wasn't reaped quickly
    enough, and on real hardware that fallback landed mid
    framebuffer/heap access and hung the entire board, not just the one
    task, on this flat-memory build. A launched app can only be closed if
    it cooperates by installing its own SIGTERM handler
  • toast notifications (nxstore_toast()) for install/uninstall/launch
    outcomes and app-closed events, additive to the durable per-row
    subtitle text
  • a bounded (15s) wait on DHCP completion before the boot-time catalog
    sync, since Wi-Fi association completing doesn't imply DHCP has —
    fetching before that resolves fails with -ENETUNREACH, which is
    indistinguishable from being genuinely offline without the wait

Impact

User impact:

  • adds a new nxstore command: an on-device, touchscreen graphical
    front-end for browsing, installing, launching, and closing nxpkg
    packages, instead of driving the same flow from the nxpkg CLI by
    hand

Build impact:

Runtime impact:

  • needs a working framebuffer (/dev/fb0) and touch input device
    (/dev/input0)
  • spawns launched packages with a fixed 32 KiB stack
    (NXSTORE_LAUNCH_STACKSIZE) — posix_spawn's own default
    (CONFIG_POSIX_SPAWN_DEFAULT_STACKSIZE, 2 KiB) is far too small for
    a real LVGL/framebuffer app and overflowing it does not fail loudly

Compatibility impact:

  • none for existing applications unless CONFIG_SYSTEM_NXSTORE is
    enabled

Testing

Host used for build:

  • OS: macOS 26.5 arm64
  • compiler: xtensa-esp-elf-gcc (crosstool-NG esp-14.2.0_20241119) 14.2.0

Target used for verification:

  • arch: xtensa
  • board/config used during runtime verification: esp32s3-touch-lcd-7:nsh
  • board: Waveshare ESP32-S3-Touch-LCD-7 (custom board directory, not
    yet upstream)

Runtime flow verified on hardware, repeatedly, across the packages this
project has shipped on top of nxstore (a calculator, Conway's Game of
Life, a matching game, snake, brick breaker, and a Doom port — all
separate follow-on PRs):

  1. boot to the touchscreen app list, catalog synced automatically at boot
  2. tap an uninstalled package's card — install runs in the background
    with a progress indicator, toast on completion
  3. tap the now-installed package's card — it launches, and the
    supervisor bar with the Close button appears at the top of the
    screen
  4. play/use the launched app for a few seconds
  5. tap Close — SIGTERM is sent, the app reaps itself, and the app
    list reappears cleanly

Specifically re-verified in this project's most recent hardware pass,
directly relevant to close_running_app_event_cb() in this PR: launched
a package with a previously-known Close-button regression (its own
rendering was painting over the reserved top bar every frame), tapped
Close, and confirmed via syslog that the sequence in this file executed
correctly end-to-end:

nxstore: close cb fired, active=1 pid=13
nxstore: close SIGTERM sent to 13
nxstore: close reaped=0 gone=1 after 2 tries

This is a draft PR, stacked on the not-yet-merged #3642 — opening now to
get the series in front of reviewers.

Fill in most of what the initial nxpkg slice deferred: network sync
and artifact acquisition over plain HTTP (verified via SHA-256), an
install rewrite that supports network sources and reclaims state left
by an interrupted previous install, and wiring update/remove/rollback/
available into the CLI.

Harden the package path against untrusted input along the way: bounded
memory-safety helpers and explicit size limits throughout, storage
read/write hardened against partially-written files, path-traversal
rejection in manifest name/version before they reach the filesystem,
and a fix for a lost-update race on the shared installed-packages
database (two concurrent installs of different packages could
otherwise silently clobber each other's recorded state).

Add an optional manifest icon field for the nxstore GUI frontend to
consume, raise PKG_INDEX_MAX now that the in-memory index is
heap-allocated, and document the repository layout and local server
setup in system/nxpkg/README.txt.

Also fixes a real build break: pkg_runtime_compat() unconditionally
referenced CONFIG_ARCH_BOARD, a Kconfig string symbol with no default
clause under ARCH_BOARD_CUSTOM, so it is left entirely undefined
rather than defined-but-empty on a custom board - falls back to
CONFIG_ARCH_BOARD_CUSTOM_NAME instead. Routes pkg_error()/pkg_info()
through syslog rather than stdio, since neither is visible to a
supervisor with no attached console.

Signed-off-by: aviralgarg05 <gargaviral99@gmail.com>
Add nxstore, an LVGL-based frontend for nxpkg (system/nxpkg): lists
packages from the local index, installs/launches the selected one,
and supervises whatever it hands the screen to.

Card-based app list (populate_app_list()): each row shows name,
version, and description/status, install/launch state reflected via
icon glyph and color (LV_SYMBOL_DOWNLOAD/PLAY, accent/success color),
and a sliding-segment progress bar during install (no real byte-level
progress is available from pkg_install(), so this reads as
'actively working' without fabricating a percentage). Explicit
LV_STATE_PRESSED styling on every tappable row/button, since no LVGL
theme is loaded and a tap would otherwise give no visual feedback at
all.

Supervisor screen (build_run_screen()/nxstore_enter_running_screen()):
a launched app (e.g. a game that owns /dev/fb0 directly, not just
another LVGL client) gets a dedicated screen with a name label and a
Close button, confined to the border region the launched app's own
scaled/centered framebuffer output never draws into, so switching
back to it doesn't fight over pixels with whatever the app already
put in the framebuffer.

Close/reap handling (close_running_app_event_cb()/
nxstore_poll_running_app()): sends SIGTERM and polls waitpid(WNOHANG)
for the launched pid, but explicitly also treats waitpid() returning
ECHILD as 'already gone' rather than 'still running' - both the
close-button handler and the passive per-loop poll independently race
to reap the same child, so whichever one loses that race must not
spin forever waiting for a wait() that can now never succeed. Requires
the target app to install its own SIGTERM handler to exit cleanly
(this is why there is no generic force-kill fallback here: an
earlier version of this code called task_delete() when SIGTERM wasn't
reaped quickly enough, which was found on real hardware to hang the
entire board - not just the one task - when it landed mid
framebuffer/heap access on this flat-memory build).

Toast notifications (nxstore_toast()) provide a transient, unmissable
confirmation for install/uninstall/launch outcomes and app-closed
events, additive to the durable per-row subtitle text rather than a
replacement for it.

nxstore's own boot-time catalog sync waits (bounded, 15s) on
g_wifi_dhcp_ret before attempting a network fetch, since Wi-Fi
association completing doesn't imply DHCP has - an HTTP fetch
attempted in that window fails with -ENETUNREACH even though the
link itself is already up, indistinguishable from being genuinely
offline without this wait.

Signed-off-by: aviralgarg05 <gargaviral99@gmail.com>
@aviralgarg05
aviralgarg05 force-pushed the gsoc/nxstore-app-store-ui-pr6 branch from 1d5c451 to ce6c2e5 Compare July 16, 2026 08:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant