Skip to content

fix(zaparoo): restore HDMI on frontend launches and fix DualSense maps - #20

Open
wizzomafizzo wants to merge 7 commits into
feat/zaparoo-kiosk-modefrom
fix/zaparoo-beta3-video-input
Open

fix(zaparoo): restore HDMI on frontend launches and fix DualSense maps#20
wizzomafizzo wants to merge 7 commits into
feat/zaparoo-kiosk-modefrom
fix/zaparoo-beta3-video-input

Conversation

@wizzomafizzo

@wizzomafizzo wizzomafizzo commented Aug 30, 2026

Copy link
Copy Markdown
Member
  • Move the alt_launcher hook below the geometry block in video_fb_config(). With the hook at the top of the function, the menu process never assigned fb_width/fb_height/brd_*, so video_menu_bg() built 0x0 wallpaper images and the launch-time video_fb_enable(0) programmed a 0x0 framebuffer into the FPGA on the way into fpga_load_rbf, killing HDMI output on every game launch from the frontend. The hook still owns the enable and the kernel-module write.
  • Disable the framebuffer in alt_launcher_shutdown()'s no-child branch (launcher core only), mirroring the live-child branch, so FB_EN is never carried into do_bridge(0) from the queued-init or respawn window.
  • Probe the gamepad node, not the touchpad, when gcdb builds a merged DualSense's map. input_cb loads the shared map from whichever node reaches it first and caches it by vid/pid; probed through the Touchpad node every face button resolves to a touchpad code while the d-pad keeps working, which is the beta report of "d-pad moves, Open/Options/View do nothing" seen only with keyboard dongles present.
  • Restrict the input pre-warm to merge-base, non-touchpad nodes so it cannot seed that cache from the wrong node.
  • Bound both key loops in get_ctrl_index_maps to btn_map's size, and skip zeroed cache slots in gcdb_controller_idx where an all-zero device id matched slot 0 and was handed an all-zero map.

Based on feat/zaparoo-kiosk-mode (#19) since it touches the same files; retarget to master once that merges.

Summary by CodeRabbit

  • Bug Fixes

    • Improved controller detection for merged DualSense devices, ensuring the gamepad—not the touchpad—is used for mappings.
    • Prevented composite input devices from corrupting controller button mappings.
    • Avoided assigning invalid mappings when device identifiers are unavailable.
    • Improved framebuffer configuration sequencing to support reliable display behavior during launcher transitions and restarts.
  • Documentation

    • Added guidance covering framebuffer handling and gamepad-device detection behavior.

The alt_launcher hook sat at the top of video_fb_config(), so while the
launcher owned (or was queued to own) the HPS framebuffer the function
returned before assigning anything: fb_width, fb_height and brd_* stayed 0
for the whole menu process. video_menu_bg() then built 0x0 wallpaper images
("Warning: bg1 is 0"), and the launch-time video_fb_enable(0) took
video_fb_set()'s menu-background fallback and programmed a 0x0 framebuffer
into the FPGA on the way into fpga_load_rbf, so HDMI output died on every
game launch from the frontend.

- Move the hook below the geometry block. It still owns the enable and the
  kernel-module write, which is all it ever needed to replace, and the
  launcher re-asserts its own geometry itself.
- Disable the framebuffer in alt_launcher_shutdown()'s no-child branch too,
  mirroring the live-child branch below it. The framebuffer can be on with no
  child alive (queued init, the respawn window, a crashed frontend), and FB_EN
  carried into do_bridge(0) is the AXI deadlock fpga_load_rbf warns about.
  Launcher core only: a game core never enabled it.
gcdb resolves a mapping's bN entries by enumerating the probed evdev node's
EV_KEY bits (get_ctrl_index_maps), while hN.x hat entries are arithmetic. A
DualSense contributes a gamepad node and a Touchpad node with the same
Phys/Uniq, mergedevs() merges them, and input_cb loads the shared map from
whichever node reaches it first, caching the result by vid/pid. Probed through
the touchpad, every face button resolves to a touchpad code and only the d-pad
still works: the beta report of "d-pad moves, Open/Options/View do nothing",
seen only with keyboard dongles present, which reorder /dev/input.

- zaparoo_gcdb_probe_dev() swaps a QUIRK_DS4TOUCH probe for the group's
  QUIRK_DS4 sibling. The upstream hook in input_cb is two lines.
- The pre-warm now feeds merge-base, non-touchpad nodes only, so it cannot
  seed the cache from the wrong node either.
- get_ctrl_index_maps bounds both key loops to btn_map's size. The array holds
  KEY_MAX - BTN_JOYSTICK entries at both call sites, but the loops append every
  key bit the node advertises, and a composite dongle can exceed that and run
  into the abs_map beside it.
- gcdb_controller_idx skips zeroed cache slots. db_maps is zero-initialised, so
  a device reporting no vid/pid matched an unused slot and was handed an
  all-zero map as a hit.
@coderabbitai

coderabbitai Bot commented Aug 30, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 44930f50-7890-45c2-9d2c-e634ee8a0477

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6bd5b03b-36cb-463f-a31d-0c5e7044df27

📥 Commits

Reviewing files that changed from the base of the PR and between 80352ef and 9e96519.

📒 Files selected for processing (6)
  • ZAPAROO_FORK.md
  • gamecontroller_db.cpp
  • input.cpp
  • support/zaparoo/alt_launcher.cpp
  • support/zaparoo/launcher_input_detect.inc
  • video.cpp

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The change preserves framebuffer geometry before alternate-launcher handling, redirects DualSense GCDB probes to gamepad nodes, excludes touchpad nodes during prewarming, and hardens controller-map bounds and cache matching.

Changes

Runtime input and video handling

Layer / File(s) Summary
Framebuffer geometry and shutdown handling
video.cpp, support/zaparoo/alt_launcher.cpp, ZAPAROO_FORK.md
video_fb_config() records framebuffer dimensions and borders before invoking the launcher hook. Shutdown comments document the framebuffer state handling.
DualSense gamepad probing
support/zaparoo/launcher_input_detect.inc, input.cpp, ZAPAROO_FORK.md
GCDB probing selects an associated DualSense gamepad node instead of a touchpad node. Input mapping uses the probed device data, and prewarming excludes touchpad and non-base nodes.
GCDB map bounds and cache matching
gamecontroller_db.cpp, ZAPAROO_FORK.md
Button-map writes are bounded by the map capacity. Zeroed cache slots are skipped during controller lookup.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 9e965

This PR makes localized video-output and controller-mapping fixes; no actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant input_cb
  participant zaparoo_gcdb_probe_dev
  participant DualSenseGamepadNode
  participant gcdb_map_for_controller
  input_cb->>zaparoo_gcdb_probe_dev: Probe merged device
  zaparoo_gcdb_probe_dev->>DualSenseGamepadNode: Select associated gamepad node
  zaparoo_gcdb_probe_dev-->>input_cb: Return gamepad probe index
  input_cb->>gcdb_map_for_controller: Request controller mapping
Loading

Suggested reviewers: sorgelig, asturur

🚥 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%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 4 files. (2 skipped: 2 … 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 clearly and concisely summarizes both primary changes: restoring HDMI output during frontend launches and fixing DualSense controller mappings.
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.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 4 files. (2 skipped: 2 unsupported.)

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/zaparoo-beta3-video-input

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.

The video_fb_enable(0) added to alt_launcher_shutdown()'s no-child branch in
10c4ceb broke every game launch: black screen, and Main gone with /tmp/CORENAME
still MENU because the core never came up.

app_restart() calls alt_launcher_shutdown() a second time, after fpga_load_rbf
has already run socfpga_load and do_bridge(1), so the game core is in the FPGA
by then. The child is gone at that point, so the new branch ran, and is_menu()
is a per-process cache of orig_name (user_io.cpp:222) which is still true in the
dying menu process. video_fb_set()'s menu-background fallback then turns the
disable into spi_w(FB_EN | ...) at menu geometry, writing the HPS framebuffer
into the new core along with input_switch(0) and a module-param write. That is
the same fallback the commit message named as the original cause.

The guard comment claimed "launcher core only", but is_menu() describes the
process, not what the FPGA holds. Teardown video belongs on the live-child
branch, which runs before the reconfiguration. The video_fb_config() hook move,
which is the actual HDMI fix, is unchanged.
Every launch of a core with a per-core main= (the ini has [RA_*]
main=MiSTer_RA) ended with no Main running and a black screen. What was
running instead was /media/fat/zaparoo/frontend with the rbf and mgl as its
arguments: app_restart had exec'd the frontend binary as Main.

user_io_init passes getFullPath(cfg.main) as exe, which is a pointer into
file_io's single static full_path buffer. app_restart calls
alt_launcher_shutdown() before it reads exe, and on that path
kill_stale_frontends() (getFullPath(s_launcher_path)) and a cold
alt_launcher_installed() (FileExists) both rewrite the buffer to the
frontend path. The execl then used it.

Copy exe into a private buffer before the teardown. Three lines beside the
existing fork hook in app_restart, so no new upstream surface.
Main's stdout goes to the console after a reboot, so the first-exit-after-
boot slowness (12 s from exec to frontend spawn, versus 0.4 s on a warm
cycle, and the frontend falling back to a full-res render) could not be
captured with the /tmp log recipe.

If /media/fat/zaparoo/main.log exists, a constructor dup2's it over stdout
and stderr before main(), writes a start marker per exec, rotates to .old
above 8 MB, and starts a detached heartbeat thread writing [zt <uptime ms>]
every 250 ms so upstream's untimed output can be placed against zlog's t=.
Marker and heartbeat use dprintf: the setvbuf in service_boot.cpp may not
have run yet and is only valid before the stream's first use.

Absent file, nothing runs. New file, no upstream hooks.
A cold boot where the TV had not asserted HPD yet left the frontend
rendering full-res: read_edid() returns 0 without a word when the ADV7513
senses no HPD + monitor sense, video_init() falls back to 1280x720, and
the frontend's startup probe sizes itself against that.

Nothing corrects it afterwards. The MENU core has no HDMI interrupt pin,
so upstream's video_poll() hot-plug re-init never runs; in the captured
log the mode stayed wrong for 34 minutes, until the next core load.

Poll the transmitter's sense bits instead. The pre-spawn retry now polls
out a 3 s hold window rather than giving up after one try at 500 ms, and
a spawn that still lands on the fallback mode arms a 30 s watch behind
the running child: when the display answers, re-init and restart the
child so it probes the real mode. video_reinit() reprograms the PLL, so
it is spent only on a link down->up edge and at most 3 times per init -
a display that has no EDID to give must not be flickered for it.

Sense bits come from a private i2c_open(0x39) handle, the way
hdmi_cec.cpp already opens the same chip. No upstream hooks.
nohup over ssh left no Main running: it blocks SIGHUP but the process
still inherits the ssh session, and Main does not survive it going away.
setsid from /media/fat matches how the device starts it, and the script
now checks that it actually came up instead of reporting Done.
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.

1 participant