Skip to content

Commit 9fc8c79

Browse files
fix: remove unnecessary screen width rounding in ChangeScreenSize
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>
1 parent 5073274 commit 9fc8c79

File tree

3 files changed

+48
-13
lines changed

3 files changed

+48
-13
lines changed

server/pkg/xorg/xorg.c

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -302,12 +302,18 @@ void XGetScreenConfigurations() {
302302
}
303303

304304
// Inspired by https://github.com/raboof/xrandr/blob/master/xrandr.c
305-
void XCreateScreenMode(int width, int height, short rate) {
305+
// width and height are in/out: on return they contain the actual dimensions
306+
// of the created mode (libxcvt may round width to a multiple of 8).
307+
void XCreateScreenMode(int *width, int *height, short rate) {
306308
Display *display = getXDisplay();
307309
Window root = DefaultRootWindow(display);
308310

309311
// create new mode info
310-
XRRModeInfo *mode_info = XCreateScreenModeInfo(width, height, rate);
312+
XRRModeInfo *mode_info = XCreateScreenModeInfo(*width, *height, rate);
313+
314+
// write back the actual dimensions that were created
315+
*width = mode_info->width;
316+
*height = mode_info->height;
311317

312318
// create new mode
313319
RRMode mode = XRRCreateMode(display, root, mode_info);
@@ -325,16 +331,19 @@ void XCreateScreenMode(int width, int height, short rate) {
325331

326332
// Inspired by https://fossies.org/linux/xwayland/hw/xwayland/xwayland-cvt.c
327333
XRRModeInfo *XCreateScreenModeInfo(int hdisplay, int vdisplay, short vrefresh) {
328-
char name[128];
329-
snprintf(name, sizeof name, "%dx%d_%d", hdisplay, vdisplay, vrefresh);
330-
XRRModeInfo *modeinfo = XRRAllocModeInfo(name, strlen(name));
331-
332334
#ifdef _LIBCVT_H_
333335
struct libxcvt_mode_info *mode_info;
334336

335337
// get screen mode from libxcvt, if available
338+
// NOTE: libxcvt may round hdisplay up to a multiple of 8 (CVT convention).
339+
// We use the actual output dimensions for both the mode name and geometry
340+
// so that they are consistent.
336341
mode_info = libxcvt_gen_mode_info(hdisplay, vdisplay, vrefresh, false, false);
337342

343+
char name[128];
344+
snprintf(name, sizeof name, "%dx%d_%d", mode_info->hdisplay, mode_info->vdisplay, vrefresh);
345+
XRRModeInfo *modeinfo = XRRAllocModeInfo(name, strlen(name));
346+
338347
modeinfo->width = mode_info->hdisplay;
339348
modeinfo->height = mode_info->vdisplay;
340349
modeinfo->dotClock = mode_info->dot_clock * 1000;
@@ -349,6 +358,10 @@ XRRModeInfo *XCreateScreenModeInfo(int hdisplay, int vdisplay, short vrefresh) {
349358
free(mode_info);
350359
#else
351360
// fallback to a simple mode without refresh rate
361+
char name[128];
362+
snprintf(name, sizeof name, "%dx%d_%d", hdisplay, vdisplay, vrefresh);
363+
XRRModeInfo *modeinfo = XRRAllocModeInfo(name, strlen(name));
364+
352365
modeinfo->width = hdisplay;
353366
modeinfo->height = vdisplay;
354367
#endif

server/pkg/xorg/xorg.go

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,6 @@ func ChangeScreenSize(s types.ScreenSize) (types.ScreenSize, error) {
201201
mu.Lock()
202202
defer mu.Unlock()
203203

204-
// round width to 8, because of Xorg
205-
s.Width = s.Width - (s.Width % 8)
206-
207204
// if rate is 0, set it to 60
208205
if s.Rate == 0 {
209206
s.Rate = 60
@@ -212,16 +209,33 @@ func ChangeScreenSize(s types.ScreenSize) (types.ScreenSize, error) {
212209
// convert variables to C types
213210
c_width, c_height, c_rate := C.int(s.Width), C.int(s.Height), C.short(s.Rate)
214211

215-
// if screen configuration already exists, just set it
212+
// try to set the screen configuration with the exact requested dimensions
216213
status := C.XSetScreenConfiguration(c_width, c_height, c_rate)
214+
215+
// if the exact dimensions don't match an existing mode, the width may not
216+
// be a multiple of 8 (Xorg/CVT convention). Try rounding up to the nearest
217+
// multiple of 8 before creating a new mode, since predefined modelines in
218+
// xorg.conf typically use aligned widths (e.g. 390 -> 392).
219+
if status != C.RRSetConfigSuccess {
220+
alignedWidth := roundUpTo8(s.Width)
221+
if alignedWidth != s.Width {
222+
c_width = C.int(alignedWidth)
223+
status = C.XSetScreenConfiguration(c_width, c_height, c_rate)
224+
}
225+
}
226+
227+
// if still no match, dynamically create a new mode via libxcvt
217228
if status != C.RRSetConfigSuccess {
218-
// create new screen configuration
219-
C.XCreateScreenMode(c_width, c_height, c_rate)
229+
C.XCreateScreenMode(&c_width, &c_height, c_rate)
220230

221231
// screen configuration should exist now, set it
222232
status = C.XSetScreenConfiguration(c_width, c_height, c_rate)
223233
}
224234

235+
// update s with the actual dimensions that were set
236+
s.Width = int(c_width)
237+
s.Height = int(c_height)
238+
225239
var err error
226240

227241
// if screen configuration was not set successfully, return error
@@ -237,6 +251,14 @@ func ChangeScreenSize(s types.ScreenSize) (types.ScreenSize, error) {
237251
return s, err
238252
}
239253

254+
// roundUpTo8 rounds n up to the nearest multiple of 8.
255+
func roundUpTo8(n int) int {
256+
if r := n % 8; r != 0 {
257+
return n + (8 - r)
258+
}
259+
return n
260+
}
261+
240262
func GetScreenSize() types.ScreenSize {
241263
mu.Lock()
242264
defer mu.Unlock()

server/pkg/xorg/xorg.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ void XKey(KeySym keysym, int down);
3939
Status XSetScreenConfiguration(int width, int height, short rate);
4040
void XGetScreenConfiguration(int *width, int *height, short *rate);
4141
void XGetScreenConfigurations();
42-
void XCreateScreenMode(int width, int height, short rate);
42+
void XCreateScreenMode(int *width, int *height, short rate);
4343
XRRModeInfo *XCreateScreenModeInfo(int hdisplay, int vdisplay, short vrefresh);
4444

4545
void XSetKeyboardModifier(unsigned char mod, int on);

0 commit comments

Comments
 (0)