Fix `Cocoa_GetWindowDisplayIndex` failing and causing a catastrophic crash With the introduction of this function, it is possible that for certain monitor and window configurations, creating an SDL window will cause a native crash. ``` Crashed Thread: 0 Dispatch queue: com.apple.main-thread Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000050 Exception Codes: 0x0000000000000001, 0x0000000000000050 Exception Note: EXC_CORPSE_NOTIFY Termination Reason: Namespace SIGNAL, Code 11 Segmentation fault: 11 Terminating Process: exc handler [56627] VM Region Info: 0x50 is not in any region. Bytes before following region: 140737486737328 REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL UNUSED SPACE AT START ---> VM_ALLOCATE 7fffffe75000-7fffffe76000 [ 4K] r-x/r-x SM=ALI Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0 libSDL2.dylib 0x10247f665 SDL_UpdateFullscreenMode + 357 1 libSDL2.dylib 0x10247ec70 SDL_CreateWindow_REAL + 1504 2 ??? 0x111262de8 ??? 3 ??? 0x110c39fff ??? 4 libcoreclr.dylib 0x101fdf2a9 CallDescrWorkerInternal + 124 ``` Tracking thread from our end: https://github.com/ppy/osu-framework/issues/5190 Regressed with: https://github.com/libsdl-org/SDL/pull/5573 In testing, the window would not find a valid screen if created "hanging" off a primary display with a secondary display below it. In checking why this was the case, the `display_centre` was being calculated with a negative y origin, causing a final negative value falling outside all display bounds: ``` SDL error log [debug]: display_centre.y = -1296 + 1296 / 2 SDL error log [debug]: Display rect 0: 0 0 2560 1440 SDL error log [debug]: Display rect 1: 2560 -625 1080 2560 SDL error log [debug]: Display rect 2: 0 1440 1728 1296 ``` The method that was being used to find the current window using the frame origin/size seems unreliable, so I have opted to replace it with with a tried method (https://stackoverflow.com/a/40891902). Initial testing shows that this works with non-standard DPI screens, but further testing would be appreciated (cc @sezero / @misl6 from the original PR thread).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index dca560b..3f5c046 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -2230,9 +2230,6 @@ int
Cocoa_GetWindowDisplayIndex(_THIS, SDL_Window * window)
{ @autoreleasepool
{
- NSRect displayframe;
- SDL_Point display_center;
- SDL_Rect sdl_display_rect;
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
/* Not recognized via CHECK_WINDOW_MAGIC */
@@ -2240,23 +2237,17 @@ Cocoa_GetWindowDisplayIndex(_THIS, SDL_Window * window)
return 0;
}
- /*
- Considering that we already have the display coordinates in which the window is placed (described via displayframe)
- instead of checking in which display the window is placed, we should check which SDL display matches the display described
- via displayframe.
- */
- displayframe = data.nswindow.screen.frame;
-
- display_center.x = displayframe.origin.x + displayframe.size.width / 2;
- display_center.y = displayframe.origin.y + displayframe.size.height / 2;
-
- for (int i = 0; i < SDL_GetNumVideoDisplays(); i++){
- SDL_GetDisplayBounds(i, &sdl_display_rect);
- if (SDL_EnclosePoints(&display_center, 1, &sdl_display_rect, NULL)) {
- return i;
- }
+ NSArray *screens = [NSScreen screens];
+
+ int index = 0;
+ for (NSScreen *screen in screens) {
+ if (screen == data.nswindow.screen)
+ return index;
+
+ index++;
}
- SDL_SetError("Couldn't find the display where the window is attached to.");
+
+ SDL_SetError("Couldn't find the display where the window is attached.");
return -1;
}}