Add Kid Mode launcher for Onion OS - #1
Open
daverad wants to merge 38 commits into
Open
Conversation
A fullscreen kid-safe launcher for Onion OS that locks the device to favorites only: - src/kidsMode/kidui.c: SDL carousel (box art, d-pad nav, A to play), SELECT+START 3s hold opens a 4-digit PIN pad; reads Onion's own Roms/favourite.json - kidmode-sd/App/KidsMode: arm app for the Apps tab + ash play loop that launches games the same way runtime.sh does (core overrides, playActivity, V4 560p, save/resume) and returns to the kid UI on exit - kidmode-sd/.tmp_update/startup/kidmode_boot.sh: boot hook that blocks before MainUI while /mnt/SDCARD/.kidmode exists - RetroArch kiosk lock while armed (backup/restore), salted-SHA256 PIN with plaintext-reset escape hatch, fail-open recovery, no directory moves on the SD card - .github/workflows/build-kidmode.yml: cross-compiles kidui with the miyoomini toolchain and commits the ARM binary to kidmode-sd Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
The startup hook now ships inside App/KidsMode/ and is copied into .tmp_update/startup/ automatically on arm, so installing is just dropping one visible folder onto the card (no manual edits inside the hidden .tmp_update folder, which is easy to clobber from a computer). Adds kidmode-install.zip with the ready-to-copy SD payload. [skip ci] Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
- Optional daily play budget in 5-minute steps (timer counts consumed play time, not wall clock: sleep pauses it, reboot doesn't reset it, resets per day). Warning badges overlay the running game at 3/2/1 minutes left; at zero RetroArch gets a graceful network QUIT so Onion's auto-save preserves the exact spot, then kidui shows a friendly Time's-up screen - Parent menu behind the PIN: Exit Kid Mode / +5 minutes today / Timer per day (5-min steps) / Back; remaining-time chip on the carousel - 'Kid Mode' entry in Onion's Favorites tab for one-tap arming (filtered out of the kid carousel; fav_shortcut config to disable) - Fix config_get swallowing boolean false (jq // empty pitfall) and store_pin overwriting non-PIN config keys Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
On device, the SDL/driver stack prints messages to stdout before anything kidui writes, so the shell's first-line protocol parsing failed and every PIN confirmation looked like a cancel. kidui now writes results to /tmp/kidmode_ui_result and the loop reads that; kidui stdout goes to /tmp/kidmode_ui_log for debugging. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
…ent, unlock handoff, favorites corruption - Arming now asks for a session timer (OFF / 5-50 min in 5-min steps, default OFF) and starts a fresh budget each time - Persistent remaining-time chip overlays the top-left corner during gameplay (per-minute PNGs via imgpop), plus existing 3/2/1 badges - Box art rendered upside down on device: the shipped libSDL_rotozoom flips zoomed surfaces, so scale artwork with an in-house bilinear scaler and drop the rotozoom dependency (orientation covered by test) - PIN pad and highlights now use the active Onion theme's accent color (currentpage/grid/title color from the theme config, amber fallback) - After unlock, MainUI could paint an invisible framebuffer page; drop kidui's final flip and reset the display with 'bootScreen clear' on every hand-back path - favourite.json shortcut could glue onto a final line lacking a newline, corrupting the list and breaking MainUI search results: repair glued lines automatically, append newline-safely, and make fav_shortcut opt-in (default off) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
- Box art was still upside down on device: images load 180-degree rotated relative to text rendering on this hardware, which Onion corrects by rotating every loaded image (theme_backgroundLoad). Do the same for artwork; rects/text stay unrotated - PIN entry: A now advances to the next digit instead of submitting (too easy to submit a half-entered PIN); only START confirms - Wrong PIN now shows a brief 'Wrong PIN' message instead of silently returning to the carousel Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
The Search results section needs App/Search/data/ with its anchor .miyoocmd file present; recreating it repairs an 'Empty' results screen after the folder was lost or damaged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
imgpop overlays are erased by RetroArch's per-frame redraw, so the in-game countdown was invisible. Use RA's SHOW_MSG network command instead: budget announcement at game start, '<N> minutes left' every 10 minutes and each minute from 5 down, and a save notice at zero. The corner chip remains for non-RetroArch content. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
imgpop overlays draw in panel-native coordinates (180 degrees rotated from the viewed image) and are erased by RetroArch's per-frame redraw, producing only a brief flipped flash in the corner. Drop all in-game imgpop overlays and their PNG assets; the countdown now uses RA's OSD exclusively: budget announcement at game start, every 5 minutes, and every minute from 5 down. Force video_font_enable while armed so the messages always render. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
The previous format run raced with a concurrent push and tried to auto-commit a stale-checkout diff; code is clang-format clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
RA OSD messages last ~3s; re-pushing the same text every 2s renders as
one continuous message. The last minute now stays pinned ('1 minute
left!') until the graceful save-and-quit; earlier warnings remain
once-per-minute flashes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
- Parent menu 'Add play time' now opens the same horizontal minutes picker as arming (5-50, B cancels), shows 'Added N minutes! Time left: M minutes', and returns straight to the kid launcher - While armed, override keymap ingame_single_press to 'exit to menu' (backup/restore + keymon restart) so the MENU button saves and quits to the kid launcher instead of opening the GameSwitcher overlay, which could expose the parent's recent games Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
X on the selected game asks 'Start over?'; A launches from the beginning by skipping the auto-resume snapshot (savestate_auto_load off via appendconfig, the same mechanism runtime.sh uses for its reset-game flag; in-game battery saves untouched). Injection into the emulator launch script is cleaned up after the run and defensively on every build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
A default-title if statement inserted mid-chain in ec07d54 detached the final else branch that loads favourite.json, so the carousel started with zero games: a lone fallback '?' tile, all input ignored, no exit and nothing logged (a hang, not a crash, so fail-open never fired). Restore the chain and add a stderr marker asserting favorites load in carousel mode. Also move the RetroArch/keymap backups and timer state from App/KidsMode (where a folder-replace update deletes them, orphaning a kiosk-locked config) to Saves/kidmode/, with legacy-path fallback on restore. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
…een intro - Remove all daily-timer semantics: no day-based reset (a new arm or added time is the only refill), parent menu header says 'Time left' - Parent menu is now Exit / Add play time / Back (the Timer row was redundant with the arm-time picker) - Add play time shows a full receipt: added (new total), played so far, and time remaining, then returns straight to the kid screen - App tile renamed to 'Kids Mode' with subtitle 'Curate Games, Simple UI, Timer...'; user-visible strings updated to match - PIN-creation screen now notes that Kids Mode shows only favorited games, lists the feature set, and explains the SELECT+START parent menu gesture Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
…[skip ci] Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces Kid Mode, a fullscreen favorites-only launcher that locks a Miyoo Mini/Mini+ so a young child can use it unsupervised. Integrates with Onion OS 4.3+ via a startup hook (no MainUI patching, no SD-card reorganization) and adds parental controls: a 4-digit PIN, a per-session play timer, and a RetroArch kiosk lock.
How it works
Boot hook (
kidmode_boot.sh, self-installed to.tmp_update/startup/on first arm): runs before MainUI launches. While/mnt/SDCARD/.kidmodeexists, it blocks in the kid launcher loop — MainUI only starts after a PIN unlock removes the flag. Deleting the flag file from a computer always restores normal Onion.Kid UI (
src/kidsMode/kidui.c, SDL): fullscreen carousel of Onion's own favorites (Roms/favourite.json) with box art, big labels, and left/right browsing; A launches. Holding SELECT+START 3 s opens the PIN pad; a correct PIN opens the parent menu (Exit Kid Mode / +5 minutes / Timer / Back). Accent colors are read from the active Onion theme's config. Box art is scaled by an in-house bilinear scaler (the shippedlibSDL_rotozoomflips zoomed surfaces on-device). Results are passed to the shell via/tmp/kidmode_ui_result, not stdout, which carries SDL/driver noise on hardware.Orchestration (
kid_mode_loop.sh, ash): arming asks to set a PIN (salted SHA-256 inkidmode.json, plaintext-reset escape hatch) and shows a session timer picker (OFF / 5–50 min in 5-min steps, default OFF; each arm starts a fresh budget). Games launch exactly asruntime.shdoes — same command format, per-game core overrides, playActivity tracking, V4 560p handling — so auto-save/resume is unchanged and game exits return to the kid UI. Handles/tmp/.offOrderfor clean power-off while the runtime loop is blocked.Play timer: a background ticker counts consumed seconds (sleep pauses it; reboot doesn't reset it), persists state in
timer_state.txt, overlays a persistent remaining-time chip in the top-left corner during gameplay plus 3/2/1-minute warning badges (imgpop), and at zero sends RetroArch a networkQUITso Onion's auto-save snapshots the exact spot before a friendly "Time's up!" screen. Non-RetroArch games get a best-effort TERM.RetroArch kiosk lock while armed (settings/menus hidden; config backed up and restored on unlock).
Build workflow (
.github/workflows/build-kidmode.yml): cross-compileskiduiwith the miyoomini toolchain container and commits the ARM binary tokidmode-sd/, which holds the ready-to-copy SD payload (kidmode-install.zipat the repo root).Safety properties
favourite.jsonand breaking MainUI search results — the loop now repairs glued lines automatically, appends newline-safely, and the shortcut is opt-in ("fav_shortcut": true), default off.🤖 Generated with Claude Code
https://claude.ai/code/session_01TtJHyTgJ1TdmqhZiW5qotc