From fd34bc56f97df2288f0aaaf43d14f442cc38f3ae Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 18 Jan 2024 03:36:54 -0800 Subject: [PATCH] cocoa: Fix SDL_CocoaWindowData keyboard_focus being left pointing to a destroyed SDL window if input focus not previously reset for that window - If a window being destroyed is a child of an inactive window and was the last keyboard focus of the window, that window will be left with a stale pointer to the destroyed window that it will attempt to restore the next time that window is focused. SDL_DestroyWindow will have already taken care of moving focus if this window is the current SDL keyboard focus so this change intentionally does not set focus. - Like Cocoa_HideWindow, this attempts to move the focus to the closest parent window that is not hidden or destroying. --- src/video/cocoa/SDL_cocoawindow.m | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m index 1308eb66ec..09cd91870f 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -546,16 +546,23 @@ static void Cocoa_UpdateClipCursor(SDL_Window *window) } } -static void Cocoa_SetKeyboardFocus(SDL_Window *window) +static SDL_Window *GetTopmostWindow(SDL_Window *window) { SDL_Window *topmost = window; - SDL_CocoaWindowData *topmost_data; /* Find the topmost parent */ while (topmost->parent != NULL) { topmost = topmost->parent; } + return topmost; +} + +static void Cocoa_SetKeyboardFocus(SDL_Window *window) +{ + SDL_Window *topmost = GetTopmostWindow(window); + SDL_CocoaWindowData *topmost_data; + topmost_data = (__bridge SDL_CocoaWindowData *)topmost->driverdata; topmost_data.keyboard_focus = window; SDL_SetKeyboardFocus(window); @@ -2732,6 +2739,22 @@ void Cocoa_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) NSArray *contexts; #endif /* SDL_VIDEO_OPENGL */ + SDL_Window *topmost = GetTopmostWindow(window); + SDL_CocoaWindowData *topmost_data = (__bridge SDL_CocoaWindowData *)topmost->driverdata; + + /* Reset the input focus of the root window if this window is still set as keyboard focus. + * SDL_DestroyWindow will have already taken care of reassigning focus if this is the SDL + * keyboard focus, this ensures that an inactive window with this window set as input focus + * does not try to reference it the next time it gains focus. + */ + if (topmost_data.keyboard_focus == window) { + SDL_Window *new_focus = window; + while(new_focus->parent && (new_focus->is_hiding || new_focus->is_destroying)) { + new_focus = new_focus->parent; + } + + topmost_data.keyboard_focus = new_focus; + } if ([data.listener isInFullscreenSpace]) { [NSMenu setMenuBarVisible:YES];