SDL: Further adjust implementation of `Cocoa_GetWindowDisplayIndex`

From 6bcde52d8880565b907f3e80c317517514010c4a Mon Sep 17 00:00:00 2001
From: Dean Herbert <[EMAIL REDACTED]>
Date: Sun, 24 Jul 2022 20:19:16 +0900
Subject: [PATCH] Further adjust implementation of
 `Cocoa_GetWindowDisplayIndex`

As discussed in PR review, there may be an off-chance that the index
returned doesn't match up with SDL's display indexing.

This change ensures that the indices match and adds a safety check for
off-screen windows.
---
 src/video/SDL_video.c             | 13 +++++++++++--
 src/video/cocoa/SDL_cocoawindow.m | 30 +++++++++++++++++++++---------
 2 files changed, 32 insertions(+), 11 deletions(-)

diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 1e520b24b68..144326f5434 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -1071,11 +1071,20 @@ SDL_GetDisplay(int displayIndex)
 int
 SDL_GetWindowDisplayIndex(SDL_Window * window)
 {
+    int displayIndex = -1;
+
     CHECK_WINDOW_MAGIC(window, -1);
     if (_this->GetWindowDisplayIndex) {
-        return _this->GetWindowDisplayIndex(_this, window);
+        displayIndex = _this->GetWindowDisplayIndex(_this, window);
+    }
+
+    /* A backend implementation may fail to get a display index for the window
+     * (for example if the window is off-screen), but other code may expect it
+     * to succeed in that situation, so we fall back to a generic position-
+     * based implementation in that case. */
+    if (displayIndex >= 0) {
+        return displayIndex;
     } else {
-        int displayIndex;
         int i, dist;
         int closest = -1;
         int closest_dist = 0x7FFFFFFF;
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index 3f5c046319d..157353cf0da 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -2230,25 +2230,37 @@ - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
 Cocoa_GetWindowDisplayIndex(_THIS, SDL_Window * window)
 { @autoreleasepool
 {
+    NSScreen *screen;
     SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
 
     /* Not recognized via CHECK_WINDOW_MAGIC */
-    if (data == NULL){
+    if (data == nil) {
         return 0;
     }
 
-    NSArray *screens = [NSScreen screens];
+    /* NSWindow.screen may be nil when the window is off-screen. */
+    screen = data.nswindow.screen;
+
+    if (screen != nil) {
+        CGDirectDisplayID displayid;
+        int i;
 
-    int index = 0;
-    for (NSScreen *screen in screens) {
-        if (screen == data.nswindow.screen)
-            return index;
+        /* https://developer.apple.com/documentation/appkit/nsscreen/1388360-devicedescription?language=objc */
+        displayid = [[screen.deviceDescription objectForKey:@"NSScreenNumber"] unsignedIntValue];
 
-        index++;
+        for (i = 0; i < _this->num_displays; i++) {
+            SDL_DisplayData *displaydata = (SDL_DisplayData *)_this->displays[i].driverdata;
+            if (displaydata != NULL && displaydata->display == displayid) {
+                return i;
+            }
+        }
     }
 
-    SDL_SetError("Couldn't find the display where the window is attached.");
-    return -1;
+    /* Other code may expect SDL_GetWindowDisplayIndex to always return a valid
+     * index for a window. The higher level GetWindowDisplayIndex code will fall
+     * back to a generic position-based query if the backend implementation
+     * fails. */
+    return SDL_SetError("Couldn't find the display where the window is located.");
 }}
 
 int