SDL: Changes to macOS event handler to better interact with the running app

From 9ef0b97c6d58fe09623027d651f331eb8148bf91 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 12 Apr 2021 11:25:44 -0700
Subject: [PATCH] Changes to macOS event handler to better interact with the
 running app

- Only focus a new window when one closes if the window that was closed was an SDL window

- If the application already has a key window set that is not an SDL window, don't replace it when the application is activated

- Only register the URL event handler when SDLAppDelegate is going to be set as the applications app delegate. This is to
   be consistent with previous behavior that would only register the handler in -[SDLAppDelegate applicationDidFinishLaunching:]
   and allows the running app to opt out of the behavior by setting its own app delegate.

- The URL event handler is now removed if it was set on SDLAppDelegate dealloc
---
 src/video/cocoa/SDL_cocoaevents.m | 48 +++++++++++++++++++++++++++----
 1 file changed, 42 insertions(+), 6 deletions(-)

diff --git a/src/video/cocoa/SDL_cocoaevents.m b/src/video/cocoa/SDL_cocoaevents.m
index 9d9f6d117..f496cb24f 100644
--- a/src/video/cocoa/SDL_cocoaevents.m
+++ b/src/video/cocoa/SDL_cocoaevents.m
@@ -36,6 +36,21 @@
 #define NSAppKitVersionNumber10_8 1187
 #endif
 
+static SDL_Window *FindSDLWindowForNSWindow(NSWindow *win)
+{
+    SDL_Window *sdlwindow = NULL;
+    SDL_VideoDevice *device = SDL_GetVideoDevice();
+    if (device && device->windows) {
+        for (sdlwindow = device->windows; sdlwindow; sdlwindow = sdlwindow->next) {
+            NSWindow *nswindow = ((SDL_WindowData *) sdlwindow->driverdata)->nswindow;
+            if (win == nswindow)
+                return sdlwindow;
+        }
+    }
+
+    return sdlwindow;
+}
+
 @interface SDLApplication : NSApplication
 
 - (void)terminate:(id)sender;
@@ -145,12 +160,6 @@ - (id)init
                    selector:@selector(localeDidChange:)
                        name:NSCurrentLocaleDidChangeNotification
                      object:nil];
-
-        [[NSAppleEventManager sharedAppleEventManager]
-            setEventHandler:self
-                andSelector:@selector(handleURLEvent:withReplyEvent:)
-            forEventClass:kInternetEventClass
-                andEventID:kAEGetURL];
     }
 
     return self;
@@ -164,6 +173,13 @@ - (void)dealloc
     [center removeObserver:self name:NSApplicationDidBecomeActiveNotification object:nil];
     [center removeObserver:self name:NSCurrentLocaleDidChangeNotification object:nil];
 
+    /* Remove our URL event handler only if we set it */
+    if ([NSApp delegate] == self) {
+        [[NSAppleEventManager sharedAppleEventManager]
+            removeEventHandlerForEventClass:kInternetEventClass
+                                 andEventID:kAEGetURL];
+    }
+
     [super dealloc];
 }
 
@@ -175,6 +191,10 @@ - (void)windowWillClose:(NSNotification *)notification;
         return;
     }
 
+    /* Don't do anything if this was not an SDL window that was closed */
+    if (FindSDLWindowForNSWindow(win) == NULL)
+        return;
+
     /* HACK: Make the next window in the z-order key when the key window is
      * closed. The custom event loop and/or windowing code we have seems to
      * prevent the normal behavior: https://bugzilla.libsdl.org/show_bug.cgi?id=1825
@@ -219,6 +239,13 @@ - (void)focusSomeWindow:(NSNotification *)aNotification
         return;
     }
 
+    /* Don't do anything if the application already has a key window
+     * that is not an SDL window.
+     */
+    if ([NSApp keyWindow] && FindSDLWindowForNSWindow([NSApp keyWindow]) == NULL) {
+        return;
+    }
+
     SDL_VideoDevice *device = SDL_GetVideoDevice();
     if (device && device->windows) {
         SDL_Window *window = device->windows;
@@ -469,6 +496,15 @@ - (void)handleURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEv
          * termination into SDL_Quit, and we can't handle application:openFile:
          */
         if (![NSApp delegate]) {
+            /* Only register the URL event handler if we are being set as the
+             * app delegate to avoid replacing any existing event handler.
+             */
+            [[NSAppleEventManager sharedAppleEventManager]
+                setEventHandler:appDelegate
+                    andSelector:@selector(handleURLEvent:withReplyEvent:)
+                  forEventClass:kInternetEventClass
+                     andEventID:kAEGetURL];
+
             [(NSApplication *)NSApp setDelegate:appDelegate];
         } else {
             appDelegate->seenFirstActivate = YES;