Skip to content

Support arbitrary headful viewport sizes via dynamic modelines#277

Open
hiroTamada wants to merge 1 commit into
mainfrom
hypeship/headful-dynamic-modelines
Open

Support arbitrary headful viewport sizes via dynamic modelines#277
hiroTamada wants to merge 1 commit into
mainfrom
hypeship/headful-dynamic-modelines

Conversation

@hiroTamada
Copy link
Copy Markdown
Contributor

@hiroTamada hiroTamada commented Jun 4, 2026

Problem

PATCH /display returns 500 for any width/height that lacks a pre-baked xorg.conf modeline on the headful path. For example, requesting 1365x768:

failed to change resolution: X root verification: x root is 1366x768, want 1365x768

The resize actually succeeds — the X server snaps the odd/unsupported size to the nearest available mode (1366x768) — but waitForXRootSize's exact-equality check rejects the snap and turns a working resize into an error (and leaves the display at the snapped size). Maintaining an xorg.conf allowlist covering every possible viewport size is impractical.

Approach (generate modelines on the fly)

For the headful Xorg dummy driver (which has no cell-granularity or pixel-clock constraints — HorizSync 5–1000, VertRefresh 5–200), generate the exact modeline at request time instead of enumerating them statically:

  • generateExactModeline — synthesizes CVT-style timings that preserve the exact requested active resolution. cvt/libxcvt/gtf all round H-active down to the 8px CVT cell granularity (gtf 1365 768 → a 1368-wide mode), so they can't express odd or non-mod-8 widths; this generator keeps hdisplay/vdisplay exactly as asked.
  • ensureXorgModeline — idempotent xrandr --newmode/--addmode before applying (tolerates "already exists" for modes shipped in xorg.conf or added by a prior resize).
  • assertXorgMode — pins exact geometry via xrandr --output … --mode ….

Wired into both headful paths: setResolutionXorgViaXrandr (Neko disabled) and setResolutionViaNeko.

Why the extra assertXorgMode on the Neko path

Neko's ScreenConfigurationChange matches the requested WxH against a cached RandR mode list. A just-added modeline may not be in that cache yet, so Neko reports success but snaps the X root to its largest known mode (e.g. 3840x2160). Pinning the mode directly afterward is immediate, sticks (Neko doesn't revert it), and aligns the X root with the size already handed to Neko's capture pipeline.

Scope / safety

  • Headless (Xvfb): unaffected. Xvfb has no runtime modeline support but already handles arbitrary/odd sizes by restarting Xvfb -screen 0 WxHx24, and never calls waitForXRootSize.
  • vGPU: dynamic modelines are disallowed by the NVIDIA driver; newmode/assert fail gracefully (logged, non-fatal) and behavior is unchanged from today for that path.

Testing

Validated live in headful containers (arm64 dummy driver), both Neko-enabled and Neko-disabled. All sizes return 200 on the first try with the X root landing exactly:

request before after
1365×768 500 (snapped 1366) ✅ 1365×768
1367×769 500 ✅ 1367×769
711×1423 (portrait) 500 ✅ 711×1423
2113×1187@25 500 ✅ 2113×1187
853×1280@30 500 ✅ 853×1280

go vet clean.

Follow-ups / open questions

  • Not verified: whether Neko's H.264 encoder (live-view/recording, 4:2:0) is happy at odd dimensions. The X root + API now succeed, but the streamed/recorded path may still want even-snapping or padding.
  • Could optionally gate ensureXorgModeline on a driver/displayMode check to keep vGPU logs quiet.

Note

Medium Risk
Touches live display resize, xrandr, and Neko integration on the headful path; failures could leave wrong resolution but Xvfb and most failure modes are bounded by existing verification and non-fatal vGPU handling.

Overview
Headful PATCH /display no longer depends on pre-baked xorg.conf modes for every width/height. The API now creates and applies exact RandR modelines at request time so sizes that standard cvt/gtf cannot represent (odd widths, non–8px-aligned sizes) still match waitForXRootSize instead of snapping to the nearest baked mode and returning 500.

New helpers in display.go: generateExactModeline (CVT-style timings with exact active resolution), ensureXorgModeline (idempotent xrandr --newmode / --addmode), assertXorgMode (pin output to a named mode), and xorgOutputName (shared DUMMY0 / env override). Both headful paths call ensureXorgModeline before resize: setResolutionXorgViaXrandr and setResolutionViaNeko. On the Neko path, assertXorgMode runs after ScreenConfigurationChange because Neko may use a stale mode cache and report success while the X root stays on a larger mode.

Xvfb / headless behavior is unchanged. vGPU paths may still ignore dynamic modelines; failures there are logged and non-fatal per the PR description.

Reviewed by Cursor Bugbot for commit 31c2ad1. Bugbot is set up for automated code reviews on this repo. Configure here.

PATCH /display previously 500'd for any width/height without a pre-baked
xorg.conf modeline (e.g. 1365x768): the X server snapped to the nearest
mode (1366x768) and waitForXRootSize's exact-match check rejected it.
Maintaining an allowlist for every possible size is impractical.

Instead, generate the exact modeline on the fly for the headful "dummy"
driver. generateExactModeline synthesizes CVT-style timings that preserve
the EXACT requested active resolution (cvt/gtf/libxcvt all round H-active
to the 8px cell granularity, so they can't express odd/non-mod-8 widths).
ensureXorgModeline newmode/addmode's it idempotently before the resize.

For the Neko path, ScreenConfigurationChange matches against a cached
RandR mode list and can miss a just-added mode (reporting success but
snapping the X root to its max), so assertXorgMode pins the exact geometry
via xrandr afterward.

On vGPU (dynamic modelines disallowed) newmode/assert fail gracefully and
behavior is unchanged. Headless (Xvfb) already supports arbitrary sizes
via screen restart and is unaffected.

Co-authored-by: Cursor <cursoragent@cursor.com>
@hiroTamada hiroTamada marked this pull request as ready for review June 4, 2026 15:18
@firetiger-agent
Copy link
Copy Markdown

Firetiger deploy monitoring skipped

This PR didn't match the auto-monitor filter configured on your GitHub connection:

PRs in the kernel, infra, hypeman, and hypeship repos. kernel is a ~mono repo with many logical services underneath, ensure to focus on the implicated service for the PR

Reason: PR appears to be in a display/rendering service within a larger monorepo, but doesn't clearly indicate which of the four specified repos (kernel, infra, hypeman, hypeship) it belongs to—please confirm the repo name or opt in manually.

To monitor this PR anyway, reply with @firetiger monitor this.

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