Skip to content

fix: remove unnecessary screen width rounding in ChangeScreenSize#11

Merged
hiroTamada merged 2 commits intomasterfrom
fix/remove-screen-width-rounding
Feb 13, 2026
Merged

fix: remove unnecessary screen width rounding in ChangeScreenSize#11
hiroTamada merged 2 commits intomasterfrom
fix/remove-screen-width-rounding

Conversation

@hiroTamada
Copy link

@hiroTamada hiroTamada commented Feb 12, 2026

Summary

  • Removes the Go-level s.Width = s.Width - (s.Width % 8) rounding in ChangeScreenSize that was silently truncating requested widths (e.g. 390 → 384)
  • Makes XCreateScreenMode return the actual dimensions that libxcvt produced via in/out pointer parameters, so the Go caller uses the correct width for XSetScreenConfiguration
  • Fixes the mode name in XCreateScreenModeInfo to use libxcvt's actual output dimensions instead of the original request, preventing name/geometry mismatches

Context

The rounding was originally added to match libxcvt's CVT convention of producing widths as multiples of 8. However, CVT is a timing standard for generating timing parameters for physical monitors — not a hard requirement of X.Org or the Xorg dummy driver used for virtual displays. The rounding made it impossible to use non-multiple-of-8 resolutions like 390x844 for mobile viewports, even when valid modelines existed in xorg.conf, because Neko would turn 390 into 384 before ever searching for a mode.

Now the resolution flow is:

  1. Exact match — if a pre-defined modeline exists in xorg.conf for the exact requested dimensions (e.g. 390x844), XSetScreenConfiguration finds and uses it directly.
  2. Dynamic creation — if no match, XCreateScreenMode dynamically creates a new mode via libxcvt and reports the actual dimensions back to the caller.

Verified

Tested end-to-end in a headful chromium-headful container:

  • Requested 390x844@25Hz via the kernel-images API
  • xrandr confirmed display at exactly 390x844_25 (using a manually crafted modeline in xorg.conf that bypasses CVT rounding)
  • CDP verification confirmed screen.width=390, screen.height=844, innerWidth=390, innerHeight=807 (37px app-mode title bar)

Note

Medium Risk
Touches XRandR mode selection/creation and changes the resolution-setting flow, which could affect display configuration behavior across environments, but the change is localized to the Xorg wrapper.

Overview
ChangeScreenSize no longer truncates widths to a multiple of 8 before attempting to apply a screen mode; it now tries to set the exact requested dimensions first and only creates a new mode if no existing mode matches.

XCreateScreenMode is updated to take width/height as in/out pointers so the caller receives libxcvt’s actual generated dimensions, and XCreateScreenModeInfo now names modes using those actual dimensions to avoid name/geometry mismatches.

Written by Cursor Bugbot for commit 3f90ca6. This will update automatically on new commits. Configure here.

Copy link
Collaborator

@Sayan- Sayan- left a comment

Choose a reason for hiding this comment

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

generally lgtm! how'd you test these changes?

The Go-level `s.Width = s.Width - (s.Width % 8)` rounding was silently
truncating requested widths (e.g. 390 → 384) before attempting to find
or create a screen mode. This made it impossible to use resolutions like
392x844 for mobile viewports even when valid modelines existed.

The rounding was originally added to match libxcvt's CVT convention of
producing widths as multiples of 8, but this is a timing standard for
physical monitors — not a constraint of the Xorg dummy driver used for
virtual displays.

Now ChangeScreenSize follows a 3-step fallback:
1. Try the exact requested dimensions (e.g. 390x844)
2. If no match, round width up to the nearest multiple of 8 and retry
   (e.g. 392x844 — matches predefined modelines in xorg.conf)
3. If still no match, dynamically create a new mode via libxcvt

The C layer is also updated so XCreateScreenMode returns the actual
dimensions that libxcvt produced (via in/out pointer parameters), and
XCreateScreenModeInfo uses the actual libxcvt output dimensions for the
mode name so it matches the real mode geometry.

Co-authored-by: Cursor <cursoragent@cursor.com>
@hiroTamada hiroTamada force-pushed the fix/remove-screen-width-rounding branch from 77b82c8 to 9fc8c79 Compare February 13, 2026 00:27
The 3-step resolution flow (exact → round-up → libxcvt) had no practical
benefit from the middle step. If an exact modeline exists in xorg.conf,
step 1 finds it. If not, libxcvt creates one. The round-up retry only
helped in the contrived case where a rounded modeline existed but the
exact one didn't.

Simplify to: try exact match, then create via libxcvt.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

// if no existing mode matches, dynamically create one via libxcvt
if status != C.RRSetConfigSuccess {
// create new screen configuration
C.XCreateScreenMode(c_width, c_height, c_rate)
Copy link

Choose a reason for hiding this comment

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

Missing round-up-to-8 fallback step before mode creation

Medium Severity

The PR description documents a 3-step fallback (exact match → round-up to nearest multiple of 8 → dynamic creation), but ChangeScreenSize only implements steps 1 and 3. The round-up step is missing: after the exact XSetScreenConfiguration fails, the code jumps directly to XCreateScreenMode instead of first trying the width rounded up to the nearest multiple of 8. This means if a pre-configured mode like 392×844 already exists and 390×844 is requested, libxcvt will create a duplicate 392×844 mode instead of reusing the existing one, causing mode proliferation on every such request.

Fix in Cursor Fix in Web

@hiroTamada hiroTamada merged commit abe9ac5 into master Feb 13, 2026
4 checks passed
@hiroTamada hiroTamada deleted the fix/remove-screen-width-rounding branch February 13, 2026 02:11
hiroTamada added a commit to kernel/kernel-images that referenced this pull request Feb 13, 2026
Updates the neko base image and Go module dependency to include the
screen width rounding fix (kernel/neko#11).

Co-authored-by: Cursor <cursoragent@cursor.com>
hiroTamada added a commit to kernel/kernel-images that referenced this pull request Feb 13, 2026
## Summary
- Automatically enables Chromium `--app` mode when the requested display
width < 500px or height < 200px, removing the tab/toolbar chrome that
enforces a ~500px minimum window width
- Adds `RemoveFlagsByPrefix` and `HasFlagWithPrefix` helpers to the
`chromiumflags` package for dynamic flag management
- Adds manually crafted modelines for exact 390x844 (iPhone 14/15) and
768x1024 (tablet) viewports at 60/30/25 Hz, bypassing CVT's
multiple-of-8 width convention since the Xorg dummy driver doesn't
require it

## Context

Chromium enforces a minimum window size of approximately 500px wide due
to the tab bar and toolbar chrome. Launching with `--app=<url>` removes
this chrome, allowing the window to resize to mobile viewports like
390x844. The `PatchDisplay` endpoint now automatically toggles this flag
based on the requested dimensions and restarts Chromium when needed.

The modelines use exact pixel widths (e.g. 390 instead of 392) with
manually calculated timing parameters that keep the pixel clock above
the Xorg dummy driver's 11 MHz minimum. CVT's convention of rounding
widths to multiples of 8 is a standard for real monitor compatibility —
the dummy driver is more permissive.

Depends on kernel/neko#11 for correct width
handling (removes Neko's Go-level rounding that was truncating 390 →
384).

## Test plan
- [x] `PATCH /display` with `{"width": 390, "height": 844}` — enables
app mode, display is exactly 390x844
- [x] `PATCH /display` with `{"width": 768, "height": 1024}` — no app
mode, display is 768x1024
- [x] `PATCH /display` with `{"width": 1920, "height": 1080}` — disables
app mode if previously enabled
- [x] Verify `RemoveFlagsByPrefix` and `HasFlagWithPrefix` unit tests
pass

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes runtime Chromium flag management and automatically triggers
restarts based on viewport size, which can impact session stability and
resize behavior; also updates Neko/image dependencies that affect
display handling.
> 
> **Overview**
> Automatically toggles Chromium *app mode* during `PATCH /display`:
when the requested size is below `500x200`, the API adds
`--app=https://start.duckduckgo.com` to the runtime flags and forces a
Chromium restart; when returning to normal sizes it removes the flag.
> 
> Refactors Chromium runtime flag writing around a shared
`chromiumFlagsPath` constant + `writeChromiumFlags`, adds
`chromiumflags.RemoveFlagsByPrefix`/`HasFlagWithPrefix` helpers (with
new tests), and extends the headful Xorg dummy config with exact
`390x844` (iPhone) and `768x1024` modelines at 60/30/25 Hz. Also bumps
the `neko` base image/dependency version and ignores the built
`chromium-launcher` binary in `server/.gitignore`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
9e9dfdf. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

3 participants