PROBLEM: when libxrandr-dev is installed, HAVE_XRANDR is triggered which enables some code that modifies NSWindows to adjust their position when there is a screen change.
The computations seem to not work correctly. One thing I observed is that menus are repositioned partly offscreen because of some of the adjustments.
The problem seems to be in the value of
When libxrandr-dev is not installed, there is one NSScreen that corresponds to the entire DISPLAY of the Xserver. When libxrandr-dev is installed, one NSScreen per monitor will appear, but their sizes and offsets do not match the output of the shell command xrandr.
DISCUSSION: I believe there are some inconsistencies in the computing of NSScreens (which correspond to monitors) and the XDisplay (which can span the union of a couple of monitors.) The X11 XRANDR extension provides ways to get details about monitor configurations and GS has some support for watching for XRANDR events, but it seems to me that the way in which NSScreens are configured using XRANDR is incorrect.
When libxrandr-dev is installed, an XRANDR event is handled by
libs-back and libs-gui.
Chain of events:
XGServerEvent.m
if xEvent is a RANDR event, then
XRRUpdateConfiguration() // to update the client's X config state
[NSScreen resetScreens] // destroy existing list of screens
[NSNotify NSApplicationDidChangeScreenParameters] // tell application
in NSWindow.:
- applicationDidChangeScreenParameters
- calls [NSScreen screens] which regenerates list
in NSScreen.m
- observe [NSScreen screens]
- put this print statement
NSLog(@"TOM: SCREENS:%@", screenArray)
before last line of [NSScreen screens]
in XGServerWindow.m
put this print statement
NSLog(@"TOM: BoundsForScrreen:%@ %@", screen, NSScreenFromRect(boundsRect)
before last line of boundsForScreen:(int)screen
NOTE: method screenList in XGServerWindow.m has HAVE_XRANDR code
that is building an array called monitors[] that is examined
by boundsForScreen:(int)screen. So the problem is somewhere in
that code, I believe.
With those print statements in, I see this traces below.
It seems that the :boundsForScreen:(int)screen method is not correctly accessing screen number N. It seems to be incorrect whether it is called as the result of an xrandr event, OR when it is the initial call in the applicaiton. (This seems to rule out Xevent race conditions in a running program due to the xrandr event updates).
$ openapp SystemPreferences
// initial size of screen is correct - builtin display only
2026-01-03 08:42:31.352 SystemPreferences[59609:59609] TOM: BoundsForScreen:0 {x = 0; y = 0; width = 1920; height = 1080}
2026-01-03 08:42:31.355 SystemPreferences[59609:59609] TOM: SCREENS:("<NSScreen: 0x55c620dc7e48> number: 0 frame: {x = 0; y = 0; width = 1920; height = 1080}")
// enable external 1920x1200 display. Number of screens seems ok. Sizes are not.
2026-01-03 08:42:58.937 SystemPreferences[59609:59609] TOM: BoundsForScreen:0 {x = 0; y = 0; width = 1920; height = 1080}
2026-01-03 08:42:58.937 SystemPreferences[59609:59609] TOM: BoundsForScreen:1 {x = 0; y = 0; width = 1920; height = 1080}
2026-01-03 08:42:58.937 SystemPreferences[59609:59609] TOM: SCREENS:("<NSScreen: 0x55c6211b96a8> number: 0 frame: {x = 0; y = 0; width = 1920; height = 1080}", "<NSScreen: 0x55c6213df828> number: 1 frame: {x = 0; y = 0; width = 1920; height = 1080}")
// disable external 1920x1200 display. width is incorrect.
2026-01-03 08:43:18.582 SystemPreferences[59609:59609] TOM: BoundsForScreen:0 {x = 0; y = 0; width = 3840; height = 1200}
2026-01-03 08:43:18.582 SystemPreferences[59609:59609] TOM: SCREENS:("<NSScreen: 0x55c6213afe48> number: 0 frame: {x = 0; y = 0; width = 3840; height = 1200}")
Here is another test where I start with two monitors and then change them. The initial sizes of the monitors seems incorrect.
$ openapp SystemPreferences
// initial configuration is two screens. The sizes are not right.
2026-01-03 08:48:33.733 SystemPreferences[59769:59769] TOM: BoundsForScreen:0 {x = 0; y = 0; width = 3840; height = 1200}
2026-01-03 08:48:33.735 SystemPreferences[59769:59769] TOM: BoundsForScreen:1 {x = 0; y = 0; width = 3840; height = 1200}
2026-01-03 08:48:33.735 SystemPreferences[59769:59769] TOM: SCREENS:("<NSScreen: 0x5597821dee28> number: 0 frame: {x = 0; y = 0; width = 3840; height = 1200}", "<NSScreen: 0x5597821f2fb8> number: 1 frame: {x = 0; y = 0; width = 3840; height = 1200}")
// disable one screen. The remaining builtin display has the correct bounds
2026-01-03 08:48:46.743 SystemPreferences[59769:59769] TOM: BoundsForScreen:0 {x = 0; y = 0; width = 1920; height = 1080}
2026-01-03 08:48:46.744 SystemPreferences[59769:59769] TOM: SCREENS:("<NSScreen: 0x559782815f78> number: 0 frame: {x = 0; y = 0; width = 1920; height = 1080}")
// enable the external 1920x1200 display. things are strange.
2026-01-03 08:49:12.119 SystemPreferences[59769:59769] TOM: BoundsForScreen:0 {x = 0; y = 0; width = 3840; height = 1200}
2026-01-03 08:49:12.119 SystemPreferences[59769:59769] TOM: BoundsForScreen:1 {x = 0; y = 0; width = 3840; height = 1200}
2026-01-03 08:49:12.120 SystemPreferences[59769:59769] TOM: SCREENS:("<NSScreen: 0x55978278b9b8> number: 0 frame: {x = 0; y = 0; width = 3840; height = 1200}", "<NSScreen: 0x5597827f1e58> number: 1 frame: {x = 0; y = 0; width = 3840; height = 1200}")
// disable the external display. The remaining builtin display is correct
2026-01-03 08:49:24.248 SystemPreferences[59769:59769] TOM: BoundsForScreen:0 {x = 0; y = 0; width = 1920; height = 1080}
2026-01-03 08:49:24.248 SystemPreferences[59769:59769] TOM: SCREENS:("<NSScreen: 0x559782774f78> number: 0 frame: {x = 0; y = 0; width = 1920; height = 1080}")
If I revert to no Xrandr support by removing and rebuilding
$ sudo apt remove libxrandr-dev
$ cd libs-back
$ make
$ sudo -E make install
and recompile libs-back, a simpler behavior is implemented. In this case there
is always just one "screen", and its size represents the size of the X11 "Display"
which is the union of the monitors areas. Using arandr to change the displays
has no effect on the running program.
// both monitors are enabled. start the program
$ openapp SystemPreferences
2026-01-03 09:17:47.975 SystemPreferences[64810:64810] TOM: BoundsForScreen:0 {x = 0; y = 0; width = 3840; height = 1200}
2026-01-03 09:17:47.975 SystemPreferences[64810:64810] TOM: SCREENS:("<NSScreen: 0x5631f62b96c8> number: 0 frame: {x = 0; y = 0; width = 3840; height = 1200}")
// only one monitor is enabled. start the program.
$ openapp SystemPreferences
2026-01-03 09:18:30.651 SystemPreferences[64883:64883] TOM: BoundsForScreen:0 {x = 0; y = 0; width = 1920; height = 1080}
2026-01-03 09:18:30.652 SystemPreferences[64883:64883] TOM: SCREENS:("<NSScreen: 0x55a419ee96c8> number: 0 frame: {x = 0; y = 0; width = 1920; height = 1080}")
The program arandr was used to alter the screen setup between these two configures.

PROBLEM: when libxrandr-dev is installed, HAVE_XRANDR is triggered which enables some code that modifies NSWindows to adjust their position when there is a screen change.
The computations seem to not work correctly. One thing I observed is that menus are repositioned partly offscreen because of some of the adjustments.
The problem seems to be in the value of
When libxrandr-dev is not installed, there is one NSScreen that corresponds to the entire DISPLAY of the Xserver. When libxrandr-dev is installed, one NSScreen per monitor will appear, but their sizes and offsets do not match the output of the shell command xrandr.
DISCUSSION: I believe there are some inconsistencies in the computing of NSScreens (which correspond to monitors) and the XDisplay (which can span the union of a couple of monitors.) The X11 XRANDR extension provides ways to get details about monitor configurations and GS has some support for watching for XRANDR events, but it seems to me that the way in which NSScreens are configured using XRANDR is incorrect.
When libxrandr-dev is installed, an XRANDR event is handled by
libs-back and libs-gui.
Chain of events:
With those print statements in, I see this traces below.
It seems that the :boundsForScreen:(int)screen method is not correctly accessing screen number N. It seems to be incorrect whether it is called as the result of an xrandr event, OR when it is the initial call in the applicaiton. (This seems to rule out Xevent race conditions in a running program due to the xrandr event updates).
Here is another test where I start with two monitors and then change them. The initial sizes of the monitors seems incorrect.
If I revert to no Xrandr support by removing and rebuilding
and recompile libs-back, a simpler behavior is implemented. In this case there
is always just one "screen", and its size represents the size of the X11 "Display"
which is the union of the monitors areas. Using arandr to change the displays
has no effect on the running program.
The program
arandrwas used to alter the screen setup between these two configures.