Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions server/pkg/xorg/xorg.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,12 +302,18 @@ void XGetScreenConfigurations() {
}

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

// create new mode info
XRRModeInfo *mode_info = XCreateScreenModeInfo(width, height, rate);
XRRModeInfo *mode_info = XCreateScreenModeInfo(*width, *height, rate);

// write back the actual dimensions that were created
*width = mode_info->width;
*height = mode_info->height;

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

// Inspired by https://fossies.org/linux/xwayland/hw/xwayland/xwayland-cvt.c
XRRModeInfo *XCreateScreenModeInfo(int hdisplay, int vdisplay, short vrefresh) {
char name[128];
snprintf(name, sizeof name, "%dx%d_%d", hdisplay, vdisplay, vrefresh);
XRRModeInfo *modeinfo = XRRAllocModeInfo(name, strlen(name));

#ifdef _LIBCVT_H_
struct libxcvt_mode_info *mode_info;

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

char name[128];
snprintf(name, sizeof name, "%dx%d_%d", mode_info->hdisplay, mode_info->vdisplay, vrefresh);
XRRModeInfo *modeinfo = XRRAllocModeInfo(name, strlen(name));

modeinfo->width = mode_info->hdisplay;
modeinfo->height = mode_info->vdisplay;
modeinfo->dotClock = mode_info->dot_clock * 1000;
Expand All @@ -349,6 +358,10 @@ XRRModeInfo *XCreateScreenModeInfo(int hdisplay, int vdisplay, short vrefresh) {
free(mode_info);
#else
// fallback to a simple mode without refresh rate
char name[128];
snprintf(name, sizeof name, "%dx%d_%d", hdisplay, vdisplay, vrefresh);
XRRModeInfo *modeinfo = XRRAllocModeInfo(name, strlen(name));

modeinfo->width = hdisplay;
modeinfo->height = vdisplay;
#endif
Expand Down
14 changes: 8 additions & 6 deletions server/pkg/xorg/xorg.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,6 @@ func ChangeScreenSize(s types.ScreenSize) (types.ScreenSize, error) {
mu.Lock()
defer mu.Unlock()

// round width to 8, because of Xorg
s.Width = s.Width - (s.Width % 8)

// if rate is 0, set it to 60
if s.Rate == 0 {
s.Rate = 60
Expand All @@ -212,16 +209,21 @@ func ChangeScreenSize(s types.ScreenSize) (types.ScreenSize, error) {
// convert variables to C types
c_width, c_height, c_rate := C.int(s.Width), C.int(s.Height), C.short(s.Rate)

// if screen configuration already exists, just set it
// try to set the screen configuration with the exact requested dimensions
status := C.XSetScreenConfiguration(c_width, c_height, c_rate)

// 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

C.XCreateScreenMode(&c_width, &c_height, c_rate)

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

// update s with the actual dimensions that were set
s.Width = int(c_width)
s.Height = int(c_height)

var err error

// if screen configuration was not set successfully, return error
Expand Down
2 changes: 1 addition & 1 deletion server/pkg/xorg/xorg.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ void XKey(KeySym keysym, int down);
Status XSetScreenConfiguration(int width, int height, short rate);
void XGetScreenConfiguration(int *width, int *height, short *rate);
void XGetScreenConfigurations();
void XCreateScreenMode(int width, int height, short rate);
void XCreateScreenMode(int *width, int *height, short rate);
XRRModeInfo *XCreateScreenModeInfo(int hdisplay, int vdisplay, short vrefresh);

void XSetKeyboardModifier(unsigned char mod, int on);
Expand Down
Loading