Skip to content

video: drive capture runs without a window or event loop#268

Merged
LinuxJedi merged 2 commits into
mainfrom
fix/windowless-headless-capture
Jul 23, 2026
Merged

video: drive capture runs without a window or event loop#268
LinuxJedi merged 2 commits into
mainfrom
fix/windowless-headless-capture

Conversation

@LinuxJedi

Copy link
Copy Markdown
Collaborator

Summary

Fixes #267. The crash there was not AROS-related: --screenshot-after runs routed through the windowed App with an invisible window, so winit's event-loop setup still registered with the host display server before emulation began. On hosts without window-server access (the reporter launched Copperline from a sandboxed agent session; SSH sessions hit the same wall) NSApplication aborts inside _RegisterApplication on macOS 15, and blocks forever on macOS 26 -- so a nominally headless run could never complete.

What changed

  • App::run_headless() drives --screenshot-after / --dump-frames runs with no winit event loop, window, or display connection.
  • The scheduled-event logic is shared, not duplicated: the arming block moved out of resumed() into arm_scheduled_events(), and the firing blocks moved out of about_to_wait() into fire_scheduled_events() / fire_auto_save_state() / fire_auto_shot(); dump_frame_if_due() returns an exit flag instead of taking the event loop. Both loops call the same helpers, so captures are byte-identical by construction.
  • main() routes to the windowless loop when a capture flag is present and --control-gui is not. --save-state-after alone keeps the windowed session (a state save is a capture along the way, not a run-ender); --benchmark-until / --gdb / --control already had windowless paths.
  • Behaviour change: a machine that halts before the scheduled captures complete now fails the run with an error instead of idling forever (the invisible-window path hung).
  • Docs: docs/guide/headless.md notes capture runs need no display server.

Verification

  • Full unit suite passes, including 4 new tests covering the windowless screenshot run, frame dump, scheduled input + --record-input flushing, and the no-captures error; clippy and fmt clean; builds with and without default features.
  • A screenshot from the new path is byte-identical to one from the pre-change invisible-window path (same config, deterministic core).
  • Reproduced the issue [Bug]: Crash when running AROS ROM #267 environment with a seatbelt profile denying launch-services/window-server lookups: before this change the run aborts (macOS 15) or hangs (macOS 26); with it, the run completes and the PNG is byte-identical to an unsandboxed run.
  • Scheduled keys, --save-state-after, --dump-frames, and --load-state resume all verified through the new path at the CLI level.

--screenshot-after and --dump-frames runs used to route through the
windowed App with an invisible window, so winit's event-loop setup still
registered with the host display server. On hosts without one (SSH
sessions, sandboxes that deny window-server access) NSApplication aborts
in _RegisterApplication on macOS 15 and blocks forever on macOS 26, so a
nominally headless run could never complete.

Give App a run_headless() loop that never touches winit: the scheduled
capture and input flags are armed and fired by helpers shared verbatim
with the windowed about_to_wait path (arm_scheduled_events,
fire_scheduled_events, fire_auto_save_state, fire_auto_shot, and an
event-loop-free dump_frame_if_due), so a capture run produces
byte-identical output either way. main() routes to it whenever a capture
flag is present and --control-gui is not.

One behaviour change: a machine that halts before the scheduled captures
complete now fails the run with an error instead of idling forever, which
is what the invisible-window path did.

Fixes #267
Copilot AI review requested due to automatic review settings July 23, 2026 20:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes headless capture runs (--screenshot-after, --dump-frames) so they can complete without creating a winit window/event loop (and therefore without registering with the host display server), addressing macOS crashes/hangs in sandboxed/SSH environments (Fixes #267).

Changes:

  • Added App::run_headless() to drive scheduled capture runs entirely without winit/window/display-server access.
  • Refactored scheduled-event arming/firing into shared helpers so windowed and windowless capture paths execute the same logic.
  • Routed CLI capture runs to the new windowless loop (unless --control-gui is used), and updated headless docs + added tests covering the new path.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/video/window.rs Introduces run_headless() and extracts shared scheduled-event/capture logic to support windowless capture runs.
src/video/window/tests.rs Adds tests validating windowless screenshot, frame dump, scheduled input + recording flush, and no-capture error behavior.
src/main.rs Routes capture flags to run_headless() when --control-gui isn’t requested.
docs/guide/headless.md Updates headless documentation to note capture runs don’t require a display server.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/video/window/tests.rs Outdated
Comment thread docs/guide/headless.md Outdated
Check the full 8-byte PNG signature with starts_with so a truncated
screenshot fails the assertion instead of panicking on the slice, and
stop the headless-guide intro claiming no run opens a window: only
capture runs skip the display server, and the paragraph that says so
now carries that detail alone.
@LinuxJedi
LinuxJedi merged commit 644e14c into main Jul 23, 2026
14 checks passed
@LinuxJedi
LinuxJedi deleted the fix/windowless-headless-capture branch July 23, 2026 21:15
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.

[Bug]: Crash when running AROS ROM

2 participants