Jawaka is the UMRK launcher stack built on Catastrophe. The normal entrypoint is
jawakad, a long-lived coordinator that owns scanning, launch requests, platform
control, process handoff, and the IPC socket. The foreground UI processes are
jawaka-launcher and jawaka-menu.
Primary target today is the Miniloong Pocket 1 (MLP1). macOS remains the fast local preview loop with a generated mock SD-card tree.
jawakaddaemon with Unix-domain-socket, length-prefixed JSON IPC.- Catastrophe launcher/menu UIs with Recents, Favorites, Games, Apps, and Settings surfaces.
- SQLite library database with FTS search, games, apps, recents, favorites, and persisted settings.
- Filesystem scanner for
Roms/,Images/, and platform-guardedApps/paks. - Local box art, system icons, multiple launcher themes, game search, and a game switcher (Select on the home screen, Menu + Select in a game by default).
- MLP1 platform integration for launch lifecycle, brightness, volume, audio output, Wi-Fi, Bluetooth, ADB pin control, boot splash, secondary SD unmount, LEDs, sleep, reboot, power off, and Exit to Stock.
- SVC-1 foreground-service supervision, CTL-1 status/control IPC, declarative storage/suspend policy stops, process-group cleanup, and a dynamic Settings → Services screen.
- RetroArch helpers:
jawaka-retroarch-runner,jawaka-retroarchctl, metadata catalog support, shared config reset, command-menu integration, and in-game menu flow. - Support helpers:
jawaka-osd,jawaka-platformctl,jawaka-ledd, andjawaka-scan-smoke.
Jawaka expects Catastrophe to be available locally. The Makefile uses
../Catastrophe when it exists; otherwise set CATASTROPHE_DIR.
export CATASTROPHE_DIR=../Catastrophe
make mockgen
makeRun this once after clone if system icons are missing:
./scripts/fetch-system-icons.shMLP1 build:
make mlp1Other device Makefile targets (tg5040, tg5050, my355) are placeholders.
The daemon-owned path is the main local workflow:
make run-daemonrun-daemon runs a short automated demo. For manual testing:
make run-daemon-interactiveTo keep only jawakad running and attach UI processes yourself:
make run-daemon-only
make run-launcher
make run-menuUseful smoke/debug targets:
make phase3-fixture-scan-smoke
make mlp1-adb-smoke
make mlp1-adb-input-capture
make mlp1-adb-ra-command-smokeThe RetroArch command smoke accepts an optional local content file and
CORE_SO/CORE_INFO overrides. It stages them with RetroArch's FFmpeg runtime
libraries only below a dedicated device
/tmp directory, verifies the command channel after content rendering, saves a
gameplay screenshot and verbose log under build/mlp1-ra-command-smoke/, then
removes that exact device directory:
CORE_SO=../Cores-spruce/output/mlp1/cores/mupen64plus_next_libretro.so \
CORE_INFO=../Cores-spruce/output/mlp1/info/mupen64plus_next_libretro.info \
make mlp1-adb-ra-command-smoke CONTENT=/absolute/path/to/game.z64An isolated RetroArch system directory can be staged with SYSTEM_DIR, for
example when a core needs firmware. Its contents are removed with the rest of
the temporary device directory:
SYSTEM_DIR=/absolute/path/to/retroarch-system \
make mlp1-adb-ra-command-smoke CONTENT=/absolute/path/to/game.chdDevice staging is owned by the sibling Leaf repo. From ../Leaf:
make stage-jawaka DEVICE=mlp1 # launcher payload only
make stage DEVICE=mlp1 # launcher + RetroArch/cores + apps
make stage-refresh DEVICE=mlp1 # full stage, then restart GUI stack
make refresh-jawaka DEVICE=mlp1 # restart GUI stack onlyLeaf assembles Jawaka into:
$SDCARD_PATH/.system/leaf/platforms/mlp1/launcher/
env.sh
bin/loong_pangu # staged jawakad
bin/jawaka-launcher
bin/jawaka-menu
bin/jawaka-osd
bin/jawaka-platformctl
bin/jawaka-retroarchctl
bin/jawaka-retroarch-runner
bin/jawaka-ledd
res/
Activation is controlled by:
$SDCARD_PATH/.system/leaf/platforms/mlp1/enabled
Jawaka's ADB restore intent marker lives at:
$SDCARD_PATH/.umrk/mlp1/adb-enabled
Desktop testing follows Catastrophe's default mapping:
Arrows d-pad navigation
A face button A / select
B face button B / back
X search or context action
Y refresh/rescan or secondary action
Enter Start
Space Select / game switcher
H Menu
; / t L2 / R2 tab switching
Q desktop-only daemon shutdown
On MLP1, Jawaka reads the Loong Gamepad through its platform input proxy.
Leaf's in-game actions are Menu chords. Menu is the modifier and is fixed; the second button is the user's, set in Settings -> Controls & Feedback -> In-game Shortcuts, which also offers Disabled.
| Action | Default |
|---|---|
| Game Switcher | Menu + Select |
| Screenshot | Menu + L1 |
| Recording | Menu + R1 |
Two enabled actions cannot share a button; the picker dims a button another
action holds and names the owner. Bindings are stored as symbolic names
(l1, select, disabled) rather than numbers, because evdev codes, SDL
indices and RetroArch joypad ids disagree about which number a button is --
internal/platform/input_shortcuts.h owns that vocabulary and the MLP1 input
proxy alone maps it to evdev codes.
Settings shows physical silkscreen names, not logical roles: the point of the screen is avoiding collisions with the buttons a user bound in RetroArch, and those are physical.
RetroArch has its own hotkeys behind its own modifier, which defaults to Menu as
well (input_enable_hotkey_btn = "5") and is user-owned. Where the two
collide, Leaf wins -- but only when its handler actually claims the chord. An
unbound, disabled, or contextually unavailable action forwards the whole chord to
RetroArch unchanged, so a declined screenshot still reaches whatever the user
bound there.
Jawaka consumes the shared Leaf runtime contract from:
$SDCARD_PATH/.system/leaf/platforms/$PLATFORM/launcher/env.sh
Important variables:
| Variable | Purpose |
|---|---|
CATASTROPHE_DIR |
Catastrophe checkout used by local builds |
PLATFORM / DEVICE |
platform id, usually mac or mlp1 |
SDCARD_PATH |
mock or device SD-card root |
SDCARD_PATHS |
colon-separated SD roots, primary first |
USERDATA_PATHS, SHARED_USERDATA_PATHS |
PATH-2 per-source durable roots, aligned with SDCARD_PATHS |
ROMS_PATHS, IMAGES_PATHS, MUSIC_PATHS, VIDEO_PATHS, APPS_PATHS |
indexed plural content roots, aligned with SDCARD_PATHS |
UMRK_RUNTIME_PATH |
runtime socket and scratch directory |
UMRK_PLATFORM_PATH / SYSTEM_PATH |
platform payload root |
UMRK_INTERNAL_DATA_PATH |
launcher-owned state root |
UMRK_LAUNCHER_PATH |
launcher bundle root |
UMRK_RETROARCH_BIN, CORES_PATH, INFO_PATH |
RetroArch runtime paths |
UMRK_RETROARCH_SHADERS_DIR |
release-managed Leaf shader source bundle |
UMRK_RETROARCH_USER_SHADERS_DIR |
durable RetroArch shader browser and updater root |
JAWAKA_THEME |
local preview theme override |
JAWAKA_AUTODEMO |
1 enables the short automated run-daemon flow |
JAWAKA_AUTODEMO_DELAY_MS |
auto-demo delay, default 1200 |
When the complete PATH-2 lists validate, hello-ok.features includes
source-paths-v2; malformed, incomplete, duplicate, or misaligned lists keep
that capability absent even if UMRK_ENV_VERSION=2 was inherited.
JAWAKA_SDCARD_ROOT, JAWAKA_RUNTIME_DIR, JAWAKA_RETROARCH_BIN, and
JAWAKA_RETROARCH_CORES_DIR remain compatibility aliases. New scripts and docs
should prefer the SDCARD_PATH / UMRK_* variables from the umbrella runtime
contract.
Jawaka scans content from the Leaf/UMRK SD shape:
Roms/<SYSTEM_CODE>/<title>.<ext>
Images/<SYSTEM_CODE>/<title>.png
Music/<artist>/<album>/<track>
Apps/<platform>/<Name>.pak/
Apps/shared/<Name>.pak/
BIOS/
SATURN/ # Saturn firmware
Saves/
States/
Cheats/
.umrk/<platform>/library.db
.umrk/<platform>/services-control.db
services-control.db owns persistent Start with Leaf intent separately from
session Run/Stop state. Its schema-v2 migration records Release A's one-time
legacy SSH decision atomically with the enablement value: a valid existing SSH
config enables once, while a clean or invalid install stays disabled. The
marker survives later config restoration and never reads or changes host keys.
<SYSTEM_CODE> is matched against the platform systems.json catalog, where
each user system has one canonical public folder plus legacy aliases in its
patterns[]. Alias folders (Roms/FC → NES, Roms/MGBA → GBA, …) fold into the
one canonical system, so they show as a single library; when the same title
exists under both an alias and the canonical folder, discovery keeps one entry
and prefers the canonical-folder copy. Emulator variants are a core choice under
that one system, not separate folders.
For app paks, pak.json.platform must match the containing platform directory
or be shared. Icon paths are relative to the containing .pak directory
unless they are absolute. Flat Apps/<Name>.pak/ entries are ignored.
The current Settings tree includes:
Appearance color scheme, colors, layout (list style, fonts, font size,
tab switching), status bar
Display & Sound brightness, refresh rate (60/90/120, also picks the HDMI mode),
black frame insertion (120Hz, incl. over HDMI), HDMI output
(off/4:3/stretch, auto-switch; 720p60 or 1080p120 per refresh,
1080p120 auto-reverts unless kept), volume, audio output, test sound
Lighting MLP1 LED enable/mode/color/brightness/speed
Network Wi-Fi scan/connect/forget and ADB enable/disable
Bluetooth scan, pair/connect, disconnect/forget
Game Art scrape artwork (all/per-system, missing or replace-all), live
scrape queue, artwork-type and region priority
Accounts ScreenScraper / RetroAchievements sign-in
General startup tab, auto-sleep, boot splash, game performance,
time zone, reset RetroArch config, unmount secondary SD
Services installed/retained service status, Run/Stop and persistent
Start with Leaf controls (hidden when no service is present)
System Update and About are not in the Settings tree; they live in the System menu (the Menu-button popup), which also offers a library rescan and the session/power actions (return to launcher, sleep, exit to stock, reboot, power off).
Jawaka exports Catastrophe CAT_* appearance variables before launching
jawaka-launcher, jawaka-menu, and Catastrophe-based .pak apps. Apps should
consume the inherited environment rather than read Jawaka's SQLite DB.
Long-running apps can acquire a daemon-owned block-suspend lease over the
length-prefixed JSON socket at UMRK_DAEMON_SOCKET. The request types are
suspend-inhibit-acquire, suspend-inhibit-release, and
suspend-inhibit-status. Jawaka derives ownership from Unix peer credentials,
reaps dead holders, keeps stage-1 screen blanking active, and defers only deep
suspend until the final lease is released. Status diagnostics expose holder
reasons and age, never opaque tokens.
Use make suspend-inhibit-test suspend-inhibit-ipc-smoke for native policy and
IPC coverage. make mlp1-adb-inhibit-smoke runs the intentionally intrusive
device suspend/reap smoke after CONFIRM_SUSPEND_SMOKE=1 is supplied.
The daemon advertises relocate-games-v1 in hello-ok.features. A mover first
sends the complete stable-identity batch to library-relocate-prepare, then
uses status, commit, revert, abort, and finish operations. Prepared,
committed, and reverted batches persist in Primary-owned SQLite. During that
reservation, scans suppress only the batch's exact old/new keys and launches
of the affected game ids fail with game is relocating.
Commit and revert preserve games.id and bump library.generation exactly
once inside the mapping transaction. finish releases the reservation and
returns scan_ticket_generation; the operation becomes finished only after
a successful reconciliation scan publishes a generation greater than the
current commit/revert mapping generation.
jawaka-platformctl provides typed commands:
jawaka-platformctl capabilities
jawaka-platformctl relocate-prepare OP GENERATION ITEMS_JSON
jawaka-platformctl relocate-status OP
jawaka-platformctl relocate-commit OP
jawaka-platformctl relocate-revert OP
jawaka-platformctl relocate-abort OP
jawaka-platformctl relocate-finish OPThe client uses the full 16 MiB framed transport, strictly parses daemon JSON,
and exits nonzero for daemon errors or unexpected state/generation replies.
Socket resolution is UMRK_DAEMON_SOCKET, then JAWAKA_SOCKET_PATH, then the
runtime-derived default. Use
make relocation-test relocation-ipc-smoke for transaction and native IPC
coverage.
The MLP1 launcher payload also installs jawaka-inhibitctl. PortMaster uses
its hold command for the full lifetime of a journaled cross-card package
move, so an automatic or explicit suspend cannot interrupt an active copy or
publication phase.
The daemon advertises package-quiesce-v1 for PKG-1 callers such as Leaf's
make stage-app and direct update runner. A caller sends
package-quiesce-begin with a bounded operation_id; Jawaka snapshots every
service, blocks new service generations and foreground app launches, and does
not reply ok until every owned process group is verified absent. Stale or
unverified generations fail closed before the caller may change package bytes.
After replacement, the caller sends package-quiesce-end with the same id.
Jawaka rescans manifests while the start latch is still held, restores the
snapshotted persistent enablement, and starts only desired-enabled services.
Session-only Run state is intentionally not restored. Direct/generic Leaf
updates use the same barrier internally; the stock MLP1 reboot handoff remains
reboot-mediated.
Raw diagnostic calls are available through jawaka-platformctl:
jawaka-platformctl request \
'{"type":"package-quiesce-begin","operation_id":"manual-check"}'
jawaka-platformctl request \
'{"type":"package-quiesce-end","operation_id":"manual-check"}'scripts/mockgen.shcreates the local mock SD-card tree.third_party/cjson/is vendored and used by the IPC/config paths.third_party/catastrophe/is still a placeholder; useCATASTROPHE_DIRor an adjacent../Catastrophecheckout.- Current planning documents live in the sibling workspace repo under
umrk-workspace/plans/Jawaka/. docs/PLAN.mdanddocs/ARCHITECTURE_DECISIONS.mdare historical planning docs. Current behavior is best reflected by this README, the Makefile, and the implementation undercmd/andinternal/.
Jawaka is released under the MIT License. See LICENSE.