|
| 1 | +nxstore - an LVGL touchscreen app store for NuttX |
| 2 | +================================================= |
| 3 | + |
| 4 | + nxstore is a graphical front-end for system/nxpkg. It lists the |
| 5 | + packages available in a nxpkg repository, installs the one you tap via |
| 6 | + nxpkg, launches it, and supervises it with an on-screen "Close" |
| 7 | + control. It is the touchscreen equivalent of driving the nxpkg CLI by |
| 8 | + hand; both consume the same repository, so the repository/server setup |
| 9 | + (layout, how to serve it, populating it with tools/export_pkg_repo.py, |
| 10 | + pointing the board at it) is documented once, in system/nxpkg's |
| 11 | + README.txt, and is not repeated here. |
| 12 | + |
| 13 | + |
| 14 | +Dependencies |
| 15 | +============ |
| 16 | + |
| 17 | + Kconfig: SYSTEM_NXSTORE depends on GRAPHICS_LVGL and SYSTEM_NXPKG and |
| 18 | + selects NETUTILS_CJSON. It needs a working framebuffer (/dev/fb0) and |
| 19 | + a touch input device (/dev/input0) - the same graphics/input stack |
| 20 | + examples/lvgldemo uses. NETUTILS_CJSON is used to parse the |
| 21 | + repository index and package manifests. |
| 22 | + |
| 23 | + |
| 24 | +On-device lifecycle |
| 25 | +=================== |
| 26 | + |
| 27 | + 1. Browse - the app list is built from the repository index nxpkg |
| 28 | + fetched. Installed packages show a launch action; |
| 29 | + not-yet-installed ones show an install action. |
| 30 | + 2. Install - tapping an uninstalled package runs the nxpkg install |
| 31 | + flow (download -> sha256 verify -> transactional |
| 32 | + install) on a background worker thread, with progress |
| 33 | + shown as a toast. |
| 34 | + 3. Launch - tapping an installed package spawns its payload |
| 35 | + (posix_spawn) and switches to the "running" screen: a |
| 36 | + thin bar across the top NXSTORE_BAR_HEIGHT pixels (see |
| 37 | + include/system/nxstore_chrome.h) carrying the app name |
| 38 | + and a Close button, with the rest of the framebuffer |
| 39 | + left to the running app. |
| 40 | + 4. Close - see "Closing a running app" below. |
| 41 | + |
| 42 | + Launched apps are given NXSTORE_LAUNCH_STACKSIZE (32 KiB) of stack - |
| 43 | + posix_spawn's default (CONFIG_POSIX_SPAWN_DEFAULT_STACKSIZE, 2 KiB) is |
| 44 | + far too small for a real LVGL/framebuffer app, and overflowing it does |
| 45 | + not fail loudly (it corrupts adjacent heap, surfacing later as a crash |
| 46 | + nowhere near the real cause). |
| 47 | + |
| 48 | + |
| 49 | +Closing a running app: the cooperative-SIGTERM contract |
| 50 | +======================================================= |
| 51 | + |
| 52 | + This is the one thing an app author MUST get right to be launchable |
| 53 | + from nxstore. |
| 54 | + |
| 55 | + The Close button sends the running app SIGTERM and waits for it to |
| 56 | + reap itself. It does NOT (and cannot safely) force-kill the task: |
| 57 | + |
| 58 | + - On a build with CONFIG_SIG_DEFAULT disabled (the default), NuttX |
| 59 | + has no default action for SIGTERM at all. Delivery does nothing |
| 60 | + unless the app installs its own handler with sigaction(). |
| 61 | + - An earlier nxstore fell back to task_delete() when SIGTERM went |
| 62 | + unreaped. On real hardware that force-kill landed while the app |
| 63 | + was mid framebuffer/heap access and hung the *entire board* (not |
| 64 | + just the task), requiring a physical power cycle. That fallback |
| 65 | + was removed: there is no safe way to force-terminate an arbitrary |
| 66 | + task from the outside here. |
| 67 | + |
| 68 | + So an nxstore-launchable app must cooperate: |
| 69 | + |
| 70 | + - Install an async-signal-safe SIGTERM handler that only sets a |
| 71 | + volatile sig_atomic_t flag. |
| 72 | + - Poll that flag from its own main loop and exit cleanly (freeing |
| 73 | + the framebuffer, restoring input state, etc.) when it is set. |
| 74 | + |
| 75 | + Apps whose main loop can never block indefinitely (they run to |
| 76 | + completion on their own, or fail fast at startup) do not strictly need |
| 77 | + a handler, but any app that renders in a long-lived loop does. See |
| 78 | + these in-tree examples for the exact pattern: |
| 79 | + |
| 80 | + - games/NXDoom (i_install_quit_signal / i_poll_quit_signal) |
| 81 | + - examples/calculator |
| 82 | + - games/cgol, games/brickmatch |
| 83 | + |
| 84 | + If SIGTERM is not reaped within the timeout, nxstore keeps the running |
| 85 | + screen up rather than returning to the app list - switching back while |
| 86 | + the app might still be alive and drawing into the framebuffer would |
| 87 | + just reproduce the original hang, hidden. |
| 88 | + |
| 89 | + |
| 90 | +Configuration |
| 91 | +============= |
| 92 | + |
| 93 | + SYSTEM_NXSTORE_PROGNAME program/registration name (default nxstore) |
| 94 | + SYSTEM_NXSTORE_PRIORITY task priority (default 100) |
| 95 | + SYSTEM_NXSTORE_STACKSIZE nxstore's own stack (default 8192) |
| 96 | + |
| 97 | + NXSTORE_LAUNCH_STACKSIZE (the stack given to launched apps) and |
| 98 | + NXSTORE_BAR_HEIGHT (the reserved top bar) are compile-time constants in |
| 99 | + the source / include/system/nxstore_chrome.h rather than Kconfig |
| 100 | + options. |
0 commit comments