From dc9679cd66a5874878eef7bfd7565d7d96e28d82 Mon Sep 17 00:00:00 2001 From: armm77 Date: Sun, 21 Jun 2026 09:57:36 -0500 Subject: [PATCH] SystemKit: fix framebuffer sizing so single-axis resolution changes apply applyDisplayLayout: enlarged the X framebuffer only when BOTH dimensions grew (&&) and shrank it only when one dimension got smaller (||). A change that altered a single axis - e.g. 1400x1050 -> 1680x1050, same height - matched neither branch, so the framebuffer stayed at the old size while a wider CRTC mode was requested. XRRSetCrtcConfig then failed with BadMatch, the mode never took effect, and the display showed a black band on the right (xrandr still reported the old 1400x1050 screen). Grow the framebuffer up front to the per-axis maximum of the current and new sizes so the new mode always fits, then set it to the exact final size after all CRTCs are placed. This preserves the original VirtualBox BadMatch safeguard (the framebuffer is never shrunk below the still-current layout mid-transition) while covering single-axis and mixed grow/shrink changes. --- Frameworks/SystemKit/OSEScreen.m | 33 +++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/Frameworks/SystemKit/OSEScreen.m b/Frameworks/SystemKit/OSEScreen.m index a1ae1a979..754e368a6 100644 --- a/Frameworks/SystemKit/OSEScreen.m +++ b/Frameworks/SystemKit/OSEScreen.m @@ -1091,17 +1091,22 @@ - (BOOL)applyDisplayLayout:(NSArray *)layout [updateScreenLock lock]; - /* If new screen size is BIGGER - set new screen size here - Example: current size is 1440x900, new size 1280x960. Height is bigger - but width is smaller. In VirtualBox this leads to - X Error: - BadMatch (invalid parameter attributes). */ - if (newPixSize.width > sizeInPixels.width && + /* If either dimension GROWS, enlarge the framebuffer up front to the + per-axis maximum of the current and new sizes, so the new CRTC mode + always fits. Growing only when BOTH dimensions increase left the + framebuffer too narrow/short when just one dimension changed (e.g. + 1400x1050 -> 1680x1050): XRRSetCrtcConfig then failed with BadMatch and + the display showed a black band. Using the union (not newPixSize) keeps + the framebuffer large enough for the still-current layout during the + transition. */ + if (newPixSize.width > sizeInPixels.width || newPixSize.height > sizeInPixels.height) { - NSDebugLLog(@"Screen", @"OSEScreen: set new BIGGER screen size: START"); + NSDebugLLog(@"Screen", @"OSEScreen: grow screen size to union: START"); XRRSetScreenSize(xDisplay, xRootWindow, - (int)newPixSize.width, (int)newPixSize.height, + (int)MAX(newPixSize.width, sizeInPixels.width), + (int)MAX(newPixSize.height, sizeInPixels.height), (int)mmSize.width, (int)mmSize.height); - NSDebugLLog(@"Screen", @"OSEScreen: set new BIGGER screen size: END"); + NSDebugLLog(@"Screen", @"OSEScreen: grow screen size to union: END"); } // Set resolution and gamma to displays @@ -1143,14 +1148,16 @@ - (BOOL)applyDisplayLayout:(NSArray *)layout [display setGammaFromDescription:gamma]; } - // If new screen size is SMALLER - set new screen size here - if (newPixSize.width < sizeInPixels.width || - newPixSize.height < sizeInPixels.height) { - NSDebugLLog(@"Screen", @"OSEScreen: set new SMALLER screen size: START"); + // All CRTCs are now placed within the (possibly grown) framebuffer, so set + // it to the exact final size. This shrinks it back when growing only one + // dimension or when the new layout is smaller. + if (newPixSize.width != sizeInPixels.width || + newPixSize.height != sizeInPixels.height) { + NSDebugLLog(@"Screen", @"OSEScreen: set final screen size: START"); XRRSetScreenSize(xDisplay, xRootWindow, (int)newPixSize.width, (int)newPixSize.height, (int)mmSize.width, (int)mmSize.height); - NSDebugLLog(@"Screen", @"OSEScreen: set new SMALLER screen size: END"); + NSDebugLLog(@"Screen", @"OSEScreen: set final screen size: END"); } sizeInPixels = newPixSize;