Skip to content

feat(zaparoo): OSD menu entry to toggle CRT/HDMI launcher mode#2

Merged
asturur merged 24 commits into
masterfrom
feat/zaparoo-fb-toggle
May 10, 2026
Merged

feat(zaparoo): OSD menu entry to toggle CRT/HDMI launcher mode#2
asturur merged 24 commits into
masterfrom
feat/zaparoo-fb-toggle

Conversation

@asturur
Copy link
Copy Markdown
Member

@asturur asturur commented May 9, 2026

Summary

Adds a "CRT mode: On/Off" entry to the System OSD menu (the one shown via F12 / OSD button) that toggles the Zaparoo launcher between HDMI mode (default) and native CRT mode at runtime. Activating the entry kills the running launcher and re-spawns it with the opposite mode flag — when toggling on, the bash wrapper sets ALT_LAUNCHER_CRT=1 and the launcher restarts with --crt so it adapts to 320×240 native resolution and starts the DDR3 writer. Toggle off restarts without the flag and returns to the HPS-framebuffer HDMI path.

This is the userspace counterpart to Menu_MiSTer#3, which adds a status[9] gate on the FPGA side selecting between the original cosine pattern (default) and the native DDR reader.

Behavior

Activation Launcher state before Result
First time not running spawn with --crt, set status[9]=1, input_switch(0)
First time HDMI mode shutdown, respawn with --crt, set status[9]=1
Second time CRT mode shutdown (drops status[9], restores HPS FB mode), respawn without --crt

The entry is shown only when alt_launcher_configured() is true — same gate as the existing "Launcher" item it sits beside. Label updates to reflect current mode after activation.

Implementation

alt_launcher_toggle_crt() reuses the existing lifecycle:

void alt_launcher_toggle_crt(void)
{
    bool current_crt = alt_launcher_native_crt();
    bool target_crt  = !current_crt;

    alt_launcher_shutdown();
    alt_launcher_init(target_crt);
}

alt_launcher_shutdown() already handles SIGTERM/SIGKILL, calls disable_native_crt_path() if the launcher was in CRT mode (drops status[9], restores HPS fb mode 960×720), and resets state via reset_launcher_state().

alt_launcher_init(target_crt) then schedules a respawn picked up by the next alt_launcher_poll() tick — same path the auto-init for the "Zaparoo Launcher" core uses on core load. The bash wrapper at /tmp/alt_launcher already keys off $ALT_LAUNCHER_CRT and adds --crt to the launcher command line.

A new alt_launcher_native_crt() getter exposes the current state so the menu label can show "On" or "Off".

Test plan

  • On menu core with alt_launcher configured, activating "CRT mode: Off" starts the launcher in CRT mode (status[9]=1, FPGA reads DDR)
  • Re-activating the same entry (now showing "On") kills and restarts in HDMI mode
  • Label refreshes after toggle reflects the new state
  • Entry is hidden when alt_launcher is not configured
  • Rapid activations don't leave a zombie launcher

Out of scope

  • Persisting the toggle state across reboots
  • Any change to the launcher binary itself (already supports --crt)
  • Hotkey access (deliberately omitted — OSD path is keyboard-free)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Persistent "CRT mode" toggle added to System Settings for the alternate launcher.
    • Configurable menu bitstream name supported so custom menu images are recognized and loaded.
  • Improvements

    • Menu/OSD key behavior refined while the alternate launcher is active (more reliable routing; certain function keys suppressed).
    • Framebuffer warnings suppressed and menu auto-reopen disabled to allow reliably closing System Settings.
  • Fixes

    • Menu bitstream is now reliably reloaded when booting into the menu.

Review Change Stack

…modes

Adds `alt_launcher_toggle_crt()` which kills the current launcher (if any)
and re-spawns it with the opposite mode flag — when toggling on, the bash
wrapper sets `ALT_LAUNCHER_CRT=1` and the launcher restarts with `--crt`
so it adapts to the 320x240 native resolution and starts the DDR3 writer.
On toggle-off the launcher restarts without the flag and returns to the
HDMI HPS-framebuffer path.

Bound to KEY_F2 in HandleUI, gated on `alt_launcher_configured()` and on
being in the menu core or the dedicated Zaparoo Launcher core. F2 was
previously unmapped.

Implementation reuses the existing shutdown/init lifecycle:
  - alt_launcher_shutdown() drops status[9], clears the launcher fb_mode,
    waits SIGTERM/SIGKILL, and resets s_native_crt + s_gave_up so init
    succeeds afterwards regardless of whether the launcher was running.
  - alt_launcher_init(target_crt) schedules a respawn picked up by the
    next alt_launcher_poll() tick — same path the auto-init for the
    Zaparoo Launcher core uses.

The FPGA-side (Menu_MiSTer feat/dual-mode-native-fb) consumes status[9]
as the runtime gate selecting between the cosine pattern (default) and
the native DDR reader. This hotkey is the user-facing way to flip it.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds configured menu-RBF helpers and cfg.menu_rbf, persistent alt-launcher native-CRT state and toggle, routes menu entry into System Settings when the alt launcher is configured, updates RBF load/scanning to use the configured menu RBF, and adjusts input/OSD handling so Menu/F12 reach the OSD while other menu actions and auto-reopen are suppressed when the alt launcher owns the framebuffer.

Changes

CRT menu integration and toggle

Layer / File(s) Summary
Data / Menu-RBF helpers
cfg.h, cfg.cpp, support/zaparoo/menu_rbf.cpp, support/zaparoo/menu_rbf.h
Adds cfg.menu_rbf, INI binding, and menu_rbf_name()/is_menu_rbf() helpers for configured menu RBF names.
Public API
support/zaparoo/alt_launcher.h
Adds ALT_LAUNCHER_CRT_MENUSUB and declarations for alt_launcher_toggle_crt() and zaparoo_alt_launcher_init_for_menu(); removes default arg from alt_launcher_init.
Implementation
support/zaparoo/alt_launcher.cpp
Adds CRT persistence file, blanking helper, double-write framebuffer enable sequence, alt_launcher_native_crt(), alt_launcher_toggle_crt(), and zaparoo_alt_launcher_init_for_menu().
RBF plumbing / FPGA load
file_io.cpp, fpga_io.cpp, user_io.cpp
Use menu_rbf_name()/is_menu_rbf() for loading, scanning, and restart paths; force menu RBF reload when starting menu with configured cfg.menu_rbf; switch to zaparoo_alt_launcher_init_for_menu() for menu startup when appropriate.
Input handling & UI
input.cpp, user_io.cpp, menu.cpp
Treat alt_launcher_active() like framebuffer/video for input callbacks; remove early-return so KEY_MENU/KEY_F12 reach normal OSD routing; suppress F1/F9/F12 framebuffer/menu actions while launcher owns framebuffer; prevent auto-reopen while launcher active; route menu entry to MENU_SYSTEM1 and add CRT-mode rows and handlers calling alt_launcher_toggle_crt().

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐇 I twitch my nose at the menu light,
CRT on, CRT off — I flip it just right,
I tuck the flag, I quit and start new,
The launcher wakes with a hop and a view,
A rabbit’s small cheer for the screen’s bright delight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main feature: adding an OSD menu entry to toggle CRT/HDMI launcher mode, which aligns with all the key changes across the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/zaparoo-fb-toggle

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

asturur added 4 commits May 10, 2026 00:32
The previous commit put the F2 toggle handler in menu.cpp's HandleUI, but
keys reach HandleUI only via menu_key_set(), and that queue is only fed
when the OSD is visible (or for the special is_menu_event keys F12/MENU).
While the launcher is running the OSD is hidden, so HandleUI never sees
the F2 press and the toggle silently did nothing.

Move the toggle into user_io_kbd as a sibling case of the existing
rbf_hide_datecode F2 handler. Order is important — the rbf_hide_datecode
case stays first so it still wins when OSD is visible (a niche menu
setting unrelated to the CRT path); our case fires when OSD is hidden,
which is the actual usage scenario for toggling the launcher mode.

Also remove the now-dead F2 case from menu.cpp HandleUI.
Adds a "CRT mode: On/Off" item to the System OSD (MENU_COMMON1) right
below the existing "Launcher" entry. Activating it kills the running
launcher and respawns it with the opposite mode flag — same behaviour
the previous F2 hotkey aimed for, now navigable from the OSD so it
works without a keyboard.

Implementation:
  - New slot ALT_LAUNCHER_CRT_MENUSUB = 30 next to ALT_LAUNCHER_MENUSUB.
  - alt_launcher_native_crt() exposes the current mode for the label.
  - alt_launcher_toggle_crt() wraps shutdown + init(target) so the menu
    handler stays a one-liner; same lifecycle the auto-init path uses.
  - Activation re-enters MENU_COMMON1 so the label refreshes after the
    launcher state settles.

Gated identically to the existing Launcher entry — only shown when
alt_launcher_configured() is true.
@asturur asturur changed the title feat(zaparoo): F2 hotkey to toggle CRT/HDMI launcher mode feat(zaparoo): OSD menu entry to toggle CRT/HDMI launcher mode May 9, 2026
asturur added 4 commits May 10, 2026 00:49
…SYSTEM1)

The previous commit only added the entry to MENU_COMMON1 ("System"
submenu), which is reached by F12 on a non-menu core (or Win+F12 on the
menu core). On the regular menu core, plain F12 opens the core picker;
backing out lands you in MENU_SYSTEM1 ("System Settings") instead — that
menu didn't have the toggle, hence the option appeared invisible.

Add the same "CRT mode: On/Off" entry to MENU_SYSTEM1, gated identically
on alt_launcher_configured(). The MENU_COMMON1 entry stays so the option
is reachable from the game-core path too.
Previously F12 and KEY_MENU were both early-returned in user_io_kbd
whenever the launcher was active, so the only way to reach the MiSTer
OSD was to first kill the launcher app. That made the new "CRT mode"
toggle entry unreachable in practice — the menu was always one step
behind the user's actual workflow.

Drop F12 from the early return; keep KEY_MENU dropped because that one
is the joypad OSD button remapped by alt_launcher_fb_terminal_key for
the launcher app's own UI. F12 now falls through to is_menu_event ->
menu_key_set(KEY_F12) and the OSD overlays normally.

Input grabbing handles itself: the OSD open path already calls
user_io_osd_key_enable(1) which triggers input_switch(-1), and that
re-evaluates EVIOCGRAB as (grabbed | osd_is_visible). So while the OSD
is up the launcher stops receiving inputs (MiSTer holds the exclusive
grab); when the OSD closes user_io_osd_key_enable(0) drops the grab
and the launcher resumes reading /dev/input.

Net behavior: F12 toggles the OSD on top of the running launcher, no
exit/respawn dance needed to access System Settings or the new CRT
mode toggle.
…loses it

When alt_launcher is configured, the file picker is not the user's natural
entry point on the menu core — they want a quick overlay to flip CRT mode
or change a setting and get back to their launcher app. Make F12 behave
that way:

  - F12 on the menu core (alt_launcher configured): jumps directly into
    MENU_SYSTEM1 (System Settings) instead of opening the core picker.
  - F12 again while in System Settings: closes the OSD (back to MENU_NONE1)
    instead of opening the core picker.

Behavior on non-menu cores and on the menu core without alt_launcher
configured is unchanged. The picker is still reachable from inside System
Settings via the existing "Switch to ..." entries — we just stop forcing
it as the only entry point.

Combined with the previous commit (F12 falls through user_io_kbd while
the launcher runs) the user can press F12 to overlay the OSD on top of
their running launcher, toggle CRT mode in one step, then F12 again to
hand inputs back to the launcher.
… over launcher

Two video_fb_state() gates were unconditionally getting in the way when
the alt launcher is configured:

  1. vga_nag() was painting the "fb_terminal=0 / vga_scaler=1" warning
     on VGA every time the OSD closed (MENU_NONE1 -> vga_nag), because
     video_fb_state() is permanently true while the launcher runs in
     HDMI mode. The user has fb_terminal on by design — the warning is
     wrong for this configuration.

  2. MENU_SYSTEM1 was bailing immediately to MENU_NONE1 when
     video_fb_state() was true, so F12 in HDMI launcher mode appeared to
     do nothing — it really opened MENU_SYSTEM1 and instantly closed it,
     flashing the vga_nag warning on the way out.

Both gates now opt out when alt_launcher_configured(): the warning is
suppressed (CRT just shows the menu core's snow pattern with no overlay
when the OSD is closed), and System Settings is allowed to render as the
OSD overlay on top of the running launcher. The OSD shows on both CRT
and HDMI as before.

Combined with the prior commits, the full flow the user asked for now
works: launcher running -> CRT shows clean snow / HDMI shows the
launcher app -> F12 -> OSD overlay opens directly into System Settings
on both screens, inputs grab automatically -> F12 again -> OSD closes,
inputs return to the launcher.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
menu.cpp (1)

6729-6729: ⚡ Quick win

Replace hardcoded System-menu index 7 with ALT_LAUNCHER_CRT_MENUSUB.

MENU_COMMON uses the symbolic constant, but MENU_SYSTEM still hardcodes 7. This is brittle if indices shift.

♻️ Proposed fix
-		menumask = 0x7F;
-		if (alt_launcher_configured()) menumask |= (1ULL << 7);
+		menumask = 0x7F;
+		if (alt_launcher_configured()) menumask |= (1ULL << ALT_LAUNCHER_CRT_MENUSUB);
...
-			OsdWrite(m++, s, menusub == 7);
+			OsdWrite(m++, s, menusub == ALT_LAUNCHER_CRT_MENUSUB);
...
-			case 7:
+			case ALT_LAUNCHER_CRT_MENUSUB:
 				if (alt_launcher_configured())
 				{
 					alt_launcher_toggle_crt();
 					menustate = MENU_SYSTEM1;
 				}
 				break;

Also applies to: 6782-6786, 6880-6886

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@menu.cpp` at line 6729, Replace the hardcoded System-menu bit shift (1ULL <<
7) with the symbolic constant ALT_LAUNCHER_CRT_MENUSUB wherever the System menu
mask is built (e.g., in the block that checks alt_launcher_configured() and sets
menumask for MENU_SYSTEM and the other occurrences at the noted offsets); update
the expressions using (1ULL << 7) to instead compute the mask using (1ULL <<
ALT_LAUNCHER_CRT_MENUSUB) or an equivalent macro-based mask so the code
consistently uses ALT_LAUNCHER_CRT_MENUSUB with menumask and
alt_launcher_configured().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@menu.cpp`:
- Line 6729: Replace the hardcoded System-menu bit shift (1ULL << 7) with the
symbolic constant ALT_LAUNCHER_CRT_MENUSUB wherever the System menu mask is
built (e.g., in the block that checks alt_launcher_configured() and sets
menumask for MENU_SYSTEM and the other occurrences at the noted offsets); update
the expressions using (1ULL << 7) to instead compute the mask using (1ULL <<
ALT_LAUNCHER_CRT_MENUSUB) or an equivalent macro-based mask so the code
consistently uses ALT_LAUNCHER_CRT_MENUSUB with menumask and
alt_launcher_configured().

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8afda085-06fe-4447-8cf8-7f9605fbee62

📥 Commits

Reviewing files that changed from the base of the PR and between 0cea191 and 9c831d0.

📒 Files selected for processing (4)
  • menu.cpp
  • support/zaparoo/alt_launcher.cpp
  • support/zaparoo/alt_launcher.h
  • user_io.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • support/zaparoo/alt_launcher.cpp

asturur and others added 6 commits May 10, 2026 01:42
Three layered gates were silently swallowing F12/MENU when the alt
launcher was running in HDMI mode, leaving the previous OSD-overlay work
unreachable in practice:

  1. user_io.cpp dropped KEY_MENU outright while alt_launcher was active.
     F12 already passed through, but the user explicitly wants the joypad
     MENU button to also open the OSD. Drop the early return entirely so
     both keys go through the normal is_menu_event path.

  2. menu.cpp:1277 gated the F12/MENU case statements on
     "(!video_fb_state || vt != 2)" — i.e. we only processed the keys
     when fb_terminal was off OR we weren't on tty2. With the launcher
     running on tty2 with HDMI FB on, both subexpressions are false, the
     gate fails, the `menu` flag never gets set, and the OSD never opens.
     Extend the gate to also fire when alt_launcher_active().

  3. menu.cpp:1288 (F12 release) called video_menu_bg + video_fb_enable(0)
     unconditionally, which would have torn down the launcher's HDMI
     display on every F12 keystroke. Skip that teardown when the alt
     launcher is active — the OSD compositor sits post-ascal and overlays
     whatever the launcher is rendering without needing fb_terminal to
     drop.

Net effect: with alt_launcher running in HDMI mode, F12 (or the joypad
MENU button) opens the MiSTer OSD overlay (System Settings, per the
earlier commits in this branch); F12/MENU again closes it; the launcher
keeps painting HDMI underneath the whole time. Input grabbing toggles
automatically through the existing user_io_osd_key_enable / input_switch
hookup, so MiSTer steals inputs while the OSD is up and hands them back
when it closes.
The OSD-open trigger at MENU_NONE2 keeps the menu auto-opened on the
menu core whenever fb_terminal is off — that's the original assumption
that "the menu core is the menu screen, OSD always belongs up." It works
fine for vanilla menu core and for the launcher in HDMI mode (where
video_fb_state is true and the middle condition is false).

In CRT mode with the alt launcher running, video_fb_state is false (the
HPS framebuffer is intentionally disabled — the FPGA reads DDR for video
directly via status[9]). So the middle condition stays permanently
true: every time the user closes the OSD, the next loop iteration
re-opens it instantly. Net effect: F12/MENU appears to do nothing in
CRT mode, the OSD is stuck open.

Suppress the auto-open when alt_launcher_active(). Explicit F12 / MENU
presses still set the `menu` flag and open/close the OSD as normal —
this just stops the loop from forcing it back up.
The previous attempt opened the F-key gate for *all* function keys when
the alt launcher was active. That meant other F-keys still ran their
side effects on top of the running launcher:

  - F1 -> user_io_status_set + video_menu_bg, overwriting the launcher's
    HDMI framebuffer with a wallpaper. (Worse: alt_launcher_fb_terminal_key
    maps the joypad L2 button to KEY_F1, so any L2 press silently
    corrupted the HDMI image.)

  - F9 -> video_chvt(1) + video_fb_enable(!video_fb_state), which yanks
    the active VT off tty2 and disables fb_terminal — the launcher's
    display goes black.

  - F7/F10/F11 -> open OSD-related menus that were never intended to
    coexist with the launcher.

That's the "HDMI framebuffer kind of crash" symptom.

Restore the original strict gate for the main switch so all of the above
side effects stay disabled while the launcher owns the framebuffer. Add
a separate else-if block that handles only F12 / F12-UP when
alt_launcher_active(), with no fb_terminal teardown on release. Net
behaviour:

  - Launcher running: F12 toggles the MiSTer OSD overlay on top of the
    launcher; nothing else touches the framebuffer.
  - Launcher not active: original F-key behaviour, byte-for-byte.
…-key handling

Adds MENU_RBF ini option and support/zaparoo/menu_rbf.{cpp,h} helpers so
fpga_load_rbf, ScanDirectory, and the menu reset paths use the configured
menu core instead of the hardcoded "menu.rbf". user_io_init forces a
reload at startup when u-boot/stock loaded the system menu.rbf before us.

menu.cpp HandleUI: fold the separate alt_launcher_active() else-if back
into the main switch and gate the destructive F-keys (F1, F9, F12-up
fb teardown) with !alt_launcher_active() instead, so the launcher's
framebuffer is preserved while still allowing F12 to overlay the OSD.
In CRT launcher mode video_fb_state() returns false, so the special
input_cb path that remaps joypad buttons via mmap[] (and ultimately
calls uinp_send_key for the launcher) was being skipped — events fell
through to the per-core input[dev].map[] path, which doesn't reach
joy_digital reliably for the menu/system mapping.

Mirrors the existing fix at joy_digital line 2475: extend the gate to
include alt_launcher_active() so CRT-mode joypad presses generate
uinput keys for the launcher's tty.
The OSD CRT/HDMI toggle was ephemeral — both reboots and returning from
a core reset the launcher back to HDMI default. Persist the most-recent
toggle to config/alt_launcher_crt.bin and restore it on menu init.

Adds a dedicated zaparoo_alt_launcher_init_for_menu() entry point so the
upstream call site in user_io.cpp stays a one-line edit and avoids
overload-resolution coupling between caller and callee.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@fpga_io.cpp`:
- Around line 444-446: The write into the fixed char path[1024] in fpga_load_rbf
uses sprintf and can overflow; replace both sprintf/sprintf-like usage with
snprintf(path, sizeof(path), ...) and check the return value for truncation (ret
< 0 or ret >= sizeof(path)) and treat that as an error path (reject the name and
return/fail), and for the absolute case (name[0]=='/') similarly use snprintf
and check truncation; reference is_menu_rbf, getStorageDir, getRootDir and the
local buffer path when implementing the bounded write and rejection on
truncation.

In `@support/zaparoo/alt_launcher.cpp`:
- Around line 300-305: The shutdown path calls alt_launcher_shutdown()
synchronously and its bounded polling loops can block the cooperative scheduler;
modify alt_launcher_shutdown() to insert scheduler_yield() inside any
bounded/wait loops (e.g., the polling loops that wait for FB/HPS release or
status[9] drop) or convert the CRT toggle to an asynchronous workflow so the
coroutine can relinquish control; ensure scheduler_yield() is called on each
loop iteration or on long waits and keep alt_launcher_init(target_crt)
invocation unchanged so the caller remains cooperative.
- Around line 293-295: The toggle logic uses alt_launcher_native_crt() which
returns false when s_pid==0, causing target_crt to always be true for
pending/stopped windows; instead read the configured/current CRT state without
PID-gating. Replace the use of alt_launcher_native_crt() for determining
current_crt with a PID-agnostic accessor (e.g., a new
alt_launcher_configured_crt() or existing stored flag) that returns the real
enabled/disabled CRT setting, then compute target_crt = !current_crt; do not
change s_pid-checks in the accessor so the toggle flips the actual saved state
rather than being forced by s_pid==0.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c4152ef8-7a6c-41e4-a183-131ec857f8e8

📥 Commits

Reviewing files that changed from the base of the PR and between 6dc8bb8 and 08ccd5d.

📒 Files selected for processing (11)
  • cfg.cpp
  • cfg.h
  • file_io.cpp
  • fpga_io.cpp
  • input.cpp
  • menu.cpp
  • support/zaparoo/alt_launcher.cpp
  • support/zaparoo/alt_launcher.h
  • support/zaparoo/menu_rbf.cpp
  • support/zaparoo/menu_rbf.h
  • user_io.cpp

Comment thread fpga_io.cpp
Comment on lines 444 to 446
if(name[0] == '/') strcpy(path, name);
else sprintf(path, "%s/%s", !strcasecmp(name, "menu.rbf") ? getStorageDir(0) : getRootDir(), name);
else sprintf(path, "%s/%s", is_menu_rbf(name) ? getStorageDir(0) : getRootDir(), name);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Bound path writes in fpga_load_rbf.

Line 445 writes into a fixed 1024-byte buffer with sprintf; a long name can overflow path and corrupt stack state. Please switch to bounded writes and reject truncation.

💡 Suggested fix
-	if(name[0] == '/') strcpy(path, name);
-	else sprintf(path, "%s/%s", is_menu_rbf(name) ? getStorageDir(0) : getRootDir(), name);
+	if (name[0] == '/')
+	{
+		if (snprintf(path, sizeof(path), "%s", name) >= (int)sizeof(path)) return -1;
+	}
+	else
+	{
+		if (snprintf(path, sizeof(path), "%s/%s",
+			is_menu_rbf(name) ? getStorageDir(0) : getRootDir(), name) >= (int)sizeof(path)) return -1;
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fpga_io.cpp` around lines 444 - 446, The write into the fixed char path[1024]
in fpga_load_rbf uses sprintf and can overflow; replace both
sprintf/sprintf-like usage with snprintf(path, sizeof(path), ...) and check the
return value for truncation (ret < 0 or ret >= sizeof(path)) and treat that as
an error path (reject the name and return/fail), and for the absolute case
(name[0]=='/') similarly use snprintf and check truncation; reference
is_menu_rbf, getStorageDir, getRootDir and the local buffer path when
implementing the bounded write and rejection on truncation.

Comment thread support/zaparoo/alt_launcher.cpp
Comment on lines +300 to +305
// Shutdown drops status[9], releases the FB mode and restores HPS framebuffer
// state regardless of whether the launcher was running. After it returns we
// always have a clean slate to spawn the next launcher invocation.
alt_launcher_shutdown();
alt_launcher_init(target_crt);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid blocking cooperative scheduling during CRT toggle.

This path synchronously calls alt_launcher_shutdown(), which can wait in bounded polling loops. Add scheduler_yield() in those wait loops (or make toggle async) so UI/poll coroutines stay responsive.

As per coding guidelines, "Long-running support code in cooperative scheduler context must call scheduler_yield() to stay cooperative and allow the coroutine to switch between poll and ui threads".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@support/zaparoo/alt_launcher.cpp` around lines 300 - 305, The shutdown path
calls alt_launcher_shutdown() synchronously and its bounded polling loops can
block the cooperative scheduler; modify alt_launcher_shutdown() to insert
scheduler_yield() inside any bounded/wait loops (e.g., the polling loops that
wait for FB/HPS release or status[9] drop) or convert the CRT toggle to an
asynchronous workflow so the coroutine can relinquish control; ensure
scheduler_yield() is called on each loop iteration or on long waits and keep
alt_launcher_init(target_crt) invocation unchanged so the caller remains
cooperative.

The alt-launcher's CRT path scans out from a dedicated FPGA-mapped DDR
region at 0x3A000000 (control word + two 320x240 RGBA buffers), not from
the kernel /dev/fb0 at 0x22000000. Nothing zeros that region between
launcher sessions or across software reboots, so the prior session's
last frame stayed on screen until the launcher's writer thread had
finished Qt startup and produced its first overwrite. Zero the buffers
before flipping status[9] so the FPGA scans out black during launcher
init instead of stale pixels.

Also double-write the kernel FB mode with a 100 ms settle so the
320x240 layout is live before status[9] flips, eliminating the
launcher's first-frame size race.
Move the alt-launcher System Settings menu (Remap, Define joystick,
CRT toggle, Reboot, Exit) out of menu.cpp's MENU_SYSTEM2 block into
a new support/zaparoo/alt_launcher_menu.cpp helper. menu.cpp now
contains three single-purpose hooks:

  - hold-to-cold-reboot key gate ORs in alt_launcher_system_holding_reboot
  - MENU_SYSTEM1 early-exit calls alt_launcher_render_system_menu when
    the launcher is configured
  - MENU_SYSTEM2 dispatch translates the trimmed menu's selection to
    upstream switch cases via alt_launcher_translate_system_select

Drops the unused ALT_LAUNCHER_CRT_MENUSUB define. The trim removes
"Switch to USB", "Scripts", and "Help" rows that aren't relevant
when the launcher is the only "core" running on top of menu.rbf,
and routes Reboot through the cold-reboot hold logic.

This collapses the upstream-file diff to ~14 lines so future rebases
auto-resolve cleanly.
The fork is single-purpose, so the launcher path and the menu RBF
path are now hardcoded:

  - "zaparoo/launcher" in support/zaparoo/alt_launcher.cpp
  - "zaparoo/menu_zaparoo.rbf" in support/zaparoo/menu_rbf.cpp

alt_launcher_configured() switches from a cfg-string check to a cached
FileExists() probe of the hardcoded path, so a missing launcher binary
falls back to the upstream OSD instead of attempting to spawn nothing.

The CRT persistence file renames from alt_launcher_crt.bin to
zaparoo_launcher_crt.bin to match the user-facing "Zaparoo launcher"
naming (internal symbols keep the alt_launcher_ prefix).

Anyone with FB_TERMINAL or RECENTS in their MiSTer.ini still parses
cleanly; alt_launcher_cfg_apply() runs after ini_parse() and forces
both back to 1 since the launcher needs them on. ALT_LAUNCHER and
MENU_RBF are gone from the parser table — legacy fork users will see
a one-time "unknown option" notice and can drop those lines.
Cherry-picking commit-by-commit re-exposes intra-fork revert pairs and
parallel feature commits that have since been reconciled on master.
Mirror unstable-build.sh and apply the cumulative fork-only diff with
3-way merge fallback so shared regions where stable has drifted from
upstream master can still merge cleanly. Exclude MiSTer.ini — the fork's
only change is a no-op uncomment of a default-valued line, not worth
fighting stable's example-ini drift on every release.
@wizzomafizzo
Copy link
Copy Markdown
Member

you can remove alt_launcher from mister.ini now

you must copy the menu fork to zaparoo/menu_zaparoo.rbf

wizzomafizzo and others added 5 commits May 10, 2026 17:35
When the alt launcher exits cleanly the OSD reverts to stock menu
items for the rest of the session (until reboot, which still routes
back to the Zaparoo menu RBF). Adds an s_escaped latch in
alt_launcher_configured() and gates the cores-picker root in
SelectFile() on is_menu() so the populated cores list works while the
Zaparoo menu RBF (in zaparoo/) is still loaded.
Adds a sibling page to the trimmed System Settings menu (reachable via
right-arrow) hosting:

- H Offset (-8..+7 pixels) - +/- to adjust
- V Offset (-8..+7 lines)  - +/- to adjust
- CRT mode toggle (relocated from System Settings)
- Exit

Offsets persist via FileSaveConfig in zaparoo_video_offsets.bin (mirror
of the existing zaparoo_launcher_crt.bin pattern). Loaded and pushed to
the FPGA via user_io_status_set on menu init, so they apply on boot.

The 4-bit two's-complement bit pattern matches Menu_MiSTer's signed
status[13:10]/status[17:14] inputs which feed h_offset/v_offset on
native_video_timing.sv.

System Settings page now renders OSD_ARROW_LEFT|OSD_ARROW_RIGHT to
indicate the new sibling, and the CRT row + its dispatcher entry
have been removed (they live in display_menu now).
Living document mapping the Zaparoo-specific changes in this fork
(commits by wizzomafizzo + asturur from a2eb35e to HEAD), plus a
prioritized list of inconsistencies worth cleaning up — namespace
drift, overlapping gating predicates, scattered persistence files,
implicit cross-repo status-bit contract, etc.

Intended to evolve as the fork iterates so future contributors don't
have to re-derive the architecture from git log.
@asturur asturur merged commit 5bc1704 into master May 10, 2026
1 check passed
asturur added a commit that referenced this pull request May 10, 2026
Reflects 7d6b40a:
- right-side surface is now two pages (Launcher top + Video sub-page),
  files renamed to launcher_pages.{cpp,h}, symbols split into
  launcher_page_* / video_page_*
- Scripts row back in trimmed System Settings
- OSD auto-dismiss on launcher spawn (MenuHide in spawn())
- CRT-mode-on-exit drops to HDMI for the rest of the session

Cleanup backlog: closes #2.10 (naming drift resolved), adds #2.11 (two
OSD-dismiss paths) and #2.12 (CRT-on-exit policy).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants