SolarOS 4.16.15 manual · api
Native foreground apps
Native foreground apps are trusted ESP32-S3 modules. They use the same sessions, display ownership, keyboard shortcuts and cold-state lifecycle as built-in apps. Emulators such as Apple II Plus and Game Boy are foreground apps. Their CPU cores and guest state belong to their modules.
Quick reference
pkg installed
pkg install apple2
pkg install gameboy
apple2 /sdcard/apple2/dos33.nib
gameboy /sdcard/roms/game.gb
session create apple2 display0 /sdcard/apple2/dos33.nib
load /sdcard/apple2.app.elf /sdcard/apple2/dos33.nib
Using installed apps
Catalog entries use type = "app" and lifecycle_abi = 2. pkg install ID installs an ELF under modules/apps/ID.elf in the preferred storage volume. Run ID [arguments] from a display shell, or use session create ID display0 [arguments]. load /path/app.app.elf [arguments] also recognizes foreground metadata. The filename does not determine the lifecycle of an app that declares metadata. Lifecycle-1 apps remain synchronous commands; their code is unloaded when main returns.
Installed foreground apps appear in app discovery. An installed foreground app with a built-in app's ID takes precedence while installed. Each app can own one session. Different external apps can remain loaded in suspended sessions. Installing or removing a running or suspended foreground app is refused. Resource declaration changes to an app already discovered require a restart.
App lists and command completion share cached discovery. Package installs and removals, FTP changes, and storage commands refresh it on the next lookup. Changes made through direct file APIs may take up to 30 seconds to appear. The host still validates each ELF when launching it.
The package catalog has application, job, driver and library categories.
Developer reference: resource declaration
solar_os_native_app_abi.h defines the exported, pointer-free solar_os_native_app_manifest record. It declares the app ID, summary, GUI or TUI class, resumable flag, worker stack and priority, internal-memory and PSRAM minima, event tick interval, and optional file extensions. File extensions are space-separated lowercase suffixes such as .gb; opening a matching file passes its path as the first argument. The host validates this record before executing module code and checks it again when loading the session worker. Catalog lifecycle and ID must match the ELF metadata.
The declared memory minima are admission checks, not reserved heaps or quotas. Bulk guest state and executable images use PSRAM. An app owns its allocations and frees them before returning. Worker resources are allocated on launch; no app-specific startup hook runs at boot.
Large OS buffers use a managed PSRAM region that groups bulk buffers separately from resident executable images. mem shows its free space and largest block. Reclamation releases unused libraries; it does not discard running or suspended app state. A workload can still be refused when its allocations do not fit.
Suspend, resume and close
Alt+Tab and Alt+Right select the next session; Alt+Left selects the previous session. Display shells also provide session fg ID. A resumable module polls suspend_requested at safe points, including while paused, and calls suspend. The host joins presentation and closes foreground audio before acknowledging the switch. Guest state, the ELF image and its worker remain allocated. Resume restores display ownership, clears held input and allows the worker to continue. The module rebases its timing, restores optional audio and submits a full frame.
Ctrl+Alt+Del, Ctrl+] or session close ID requests cooperative shutdown. The module polls stop_requested, saves any state required by its app, stops its callbacks, frees allocations and returns. The host joins presentation and audio before unloading the image. A worker that fails to suspend or close within five seconds causes a device restart; it is never forcibly deleted while executing module code. Native modules run in the firmware address space.
Foreground service
main(argc, argv) obtains solar_os_native_host_v1() and requests get_service("foreground", 1, required_size). Check the returned table and its size before using appended methods. Calls belong to that app's worker; tables cannot be used by another app or a shell task.
The service provides time and bounded sleep, display palette and asynchronous frame submission, input events including release and reset, path resolution, bounded file reads and atomic replacement, optional PCM audio, exit status, and cooperative suspend and stop. Input events carry logical key bytes defined by SOLAR_OS_KEY_* in solar_os_key_codes.h, alongside physical key identity, HID usage, modifiers, and press/release state. Each app has its own input queue, presenter, audio ownership and retained state. OS session shortcuts pass through captured keyboards. Modules must not leave callbacks or tasks running after main returns.
The appended video_dimensions(&width, &height) method reports the calling app's rotated foreground graphics dimensions before video is opened. Modules that render text or other display-sized content can allocate a matching frame instead of assuming a board's resolution. Request a service table large enough to include this method. Fixed-resolution emulators can still submit their own frame size and use the presenter's aspect-preserving fit.
The appended disk_open, disk_read, disk_write, disk_sync, and disk_close methods provide exclusive access to fixed-size raw disk images. Request a table through disk_close before using them. disk_open returns an opaque handle and byte size; reads and writes take byte offsets and at most 64 KiB per call. Writes sync before returning. Claims survive suspension, and the host closes remaining handles before unloading the worker. Unmount a host FAT image before opening it as a guest disk.
Packed INDEX2 frames use four shades of the current display theme. The appended synth methods accept a bounded block size and required sample rate. Callbacks render signed 16-bit stereo PCM under a host mutex. The module uses synth_lock and synth_unlock when accessing shared synthesizer state; the host joins synth callbacks on suspend and close. The mutex remains available while suspended. Failed synth startup leaves video and emulation usable without audio.
The memory service provides PSRAM allocation and optional alloc_internal for hot state or caches. Internal allocation preserves the OS reserve and returns NULL when unavailable; modules can omit caches or fall back to PSRAM. The host also provides the current RTC register values, board-button source identity, and presenter statistics. Standard compiler helpers and setjmp/longjmp are exported for synchronous interpreter error handling.
Emulator apps
See Apple II Plus for bundled ROMs, NIB disk requirements, controls, and explicit disk saves. See Game Boy for cartridge compatibility, controls, and automatic cartridge RAM saves. See Tiny386 for bootable PC floppy and IDE images, embedded BIOS, and display requirements. See DOOM for IWAD requirements and keyboard controls. For example, with assets present:
apple2 /sdcard/apple2/dos33.nib
gameboy /sdcard/roms/game.gb
tiny386 /sdcard/tiny386/dos.img
Apple II disk writes remain in memory until explicitly saved. Session switches retain unsaved disk changes and each emulator's pause state.