diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h index 7cce828aa7..fd80ef1edb 100644 --- a/include/SDL3/SDL_video.h +++ b/include/SDL3/SDL_video.h @@ -2223,24 +2223,47 @@ extern SDL_DECLSPEC SDL_bool SDLCALL SDL_SetWindowOpacity(SDL_Window *window, fl */ extern SDL_DECLSPEC float SDLCALL SDL_GetWindowOpacity(SDL_Window *window); -/** - * Set the window as a modal to a parent window. + /** + * Set the window as a child of a parent window. * - * If the window is already modal to an existing window, it will be reparented - * to the new owner. Setting the parent window to null unparents the modal - * window and removes modal status. + * If the window is already the child of an existing window, it will be reparented + * to the new owner. Setting the parent window to null unparents the window and + * removes child window status. * - * Setting a window as modal to a parent that is a descendent of the modal + * Attempting to set the parent of a window that is currently in the modal state will fail. + * Use SDL_SetWindowModalFor() to cancel the modal status before attempting to change + * the parent. + * + * Setting a parent window that is currently the sibling or descendent of the child * window results in undefined behavior. * - * \param modal_window the window that should be set modal. - * \param parent_window the parent window for the modal window. + * \param window the window that should become the child of a parent. + * \param parent the new parent window for the child window. + * \returns SDL_TRUE on success or SDL_FALSE on failure; call + * SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_SetWindowModal + */ + extern SDL_DECLSPEC SDL_bool SDLCALL SDL_SetWindowParent(SDL_Window *window, SDL_Window *parent); + +/** + * Toggle the state of the window as modal. + * + * To enable modal status on a window, the window must currently be the child window of a parent, + * or toggling modal status on will fail. + * + * \param window the window on which to set the modal state. + * \param modal SDL_TRUE to toggle modal status on, SDL_FALSE to toggle it off. * \returns SDL_TRUE on success or SDL_FALSE on failure; call SDL_GetError() * for more information. * * \since This function is available since SDL 3.0.0. + * + * \sa SDL_SetWindowParent */ -extern SDL_DECLSPEC SDL_bool SDLCALL SDL_SetWindowModalFor(SDL_Window *modal_window, SDL_Window *parent_window); +extern SDL_DECLSPEC SDL_bool SDLCALL SDL_SetWindowModal(SDL_Window *window, SDL_bool modal); /** * Set whether the window may have input focus. diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 9ce6598ee4..f3b8de3c44 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -854,10 +854,11 @@ SDL3_0.0.0 { SDL_SetWindowKeyboardGrab; SDL_SetWindowMaximumSize; SDL_SetWindowMinimumSize; - SDL_SetWindowModalFor; + SDL_SetWindowModal; SDL_SetWindowMouseGrab; SDL_SetWindowMouseRect; SDL_SetWindowOpacity; + SDL_SetWindowParent; SDL_SetWindowPosition; SDL_SetWindowRelativeMouseMode; SDL_SetWindowResizable; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index a4b486adb5..66e618b778 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -879,10 +879,11 @@ #define SDL_SetWindowKeyboardGrab SDL_SetWindowKeyboardGrab_REAL #define SDL_SetWindowMaximumSize SDL_SetWindowMaximumSize_REAL #define SDL_SetWindowMinimumSize SDL_SetWindowMinimumSize_REAL -#define SDL_SetWindowModalFor SDL_SetWindowModalFor_REAL +#define SDL_SetWindowModal SDL_SetWindowModal_REAL #define SDL_SetWindowMouseGrab SDL_SetWindowMouseGrab_REAL #define SDL_SetWindowMouseRect SDL_SetWindowMouseRect_REAL #define SDL_SetWindowOpacity SDL_SetWindowOpacity_REAL +#define SDL_SetWindowParent SDL_SetWindowParent_REAL #define SDL_SetWindowPosition SDL_SetWindowPosition_REAL #define SDL_SetWindowRelativeMouseMode SDL_SetWindowRelativeMouseMode_REAL #define SDL_SetWindowResizable SDL_SetWindowResizable_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 7292c9c8d5..54f542f37f 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -889,10 +889,11 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_SetWindowIcon,(SDL_Window *a, SDL_Surface *b),(a,b) SDL_DYNAPI_PROC(SDL_bool,SDL_SetWindowKeyboardGrab,(SDL_Window *a, SDL_bool b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_SetWindowMaximumSize,(SDL_Window *a, int b, int c),(a,b,c),return) SDL_DYNAPI_PROC(SDL_bool,SDL_SetWindowMinimumSize,(SDL_Window *a, int b, int c),(a,b,c),return) -SDL_DYNAPI_PROC(SDL_bool,SDL_SetWindowModalFor,(SDL_Window *a, SDL_Window *b),(a,b),return) +SDL_DYNAPI_PROC(SDL_bool,SDL_SetWindowModal,(SDL_Window *a, SDL_bool b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_SetWindowMouseGrab,(SDL_Window *a, SDL_bool b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_SetWindowMouseRect,(SDL_Window *a, const SDL_Rect *b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_SetWindowOpacity,(SDL_Window *a, float b),(a,b),return) +SDL_DYNAPI_PROC(SDL_bool,SDL_SetWindowParent,(SDL_Window *a, SDL_Window *b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_SetWindowPosition,(SDL_Window *a, int b, int c),(a,b,c),return) SDL_DYNAPI_PROC(SDL_bool,SDL_SetWindowRelativeMouseMode,(SDL_Window *a, SDL_bool b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_SetWindowResizable,(SDL_Window *a, SDL_bool b),(a,b),return) diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 862405e13b..56d0d18ce4 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -264,7 +264,8 @@ struct SDL_VideoDevice bool (*GetWindowBordersSize)(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right); void (*GetWindowSizeInPixels)(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h); bool (*SetWindowOpacity)(SDL_VideoDevice *_this, SDL_Window *window, float opacity); - bool (*SetWindowModalFor)(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window); + bool (*SetWindowParent)(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent); + bool (*SetWindowModal)(SDL_VideoDevice *_this, SDL_Window *window, bool modal); void (*ShowWindow)(SDL_VideoDevice *_this, SDL_Window *window); void (*HideWindow)(SDL_VideoDevice *_this, SDL_Window *window); void (*RaiseWindow)(SDL_VideoDevice *_this, SDL_Window *window); diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index f28abf80bb..b192e18e5f 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -219,7 +219,7 @@ static void SDL_SyncIfRequired(SDL_Window *window) } } -static void SDL_SetWindowParent(SDL_Window *window, SDL_Window *parent) +static void SDL_UpdateWindowHierarchy(SDL_Window *window, SDL_Window *parent) { // Unlink the window from the existing parent. if (window->parent) { @@ -2169,6 +2169,10 @@ static void ApplyWindowFlags(SDL_Window *window, SDL_WindowFlags flags) SDL_MinimizeWindow(window); } + if (flags & SDL_WINDOW_MODAL) { + SDL_SetWindowModal(window, true); + } + if (flags & SDL_WINDOW_MOUSE_GRABBED) { SDL_SetWindowMouseGrab(window, true); } @@ -2432,10 +2436,8 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props) } _this->windows = window; - // Set the parent before creation if this is non-modal, otherwise it will be set later. - if (!(flags & SDL_WINDOW_MODAL)) { - SDL_SetWindowParent(window, parent); - } + // Set the parent before creation. + SDL_UpdateWindowHierarchy(window, parent); if (_this->CreateSDLWindow && !_this->CreateSDLWindow(_this, window, props)) { SDL_DestroyWindow(window); @@ -2462,10 +2464,6 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props) */ flags = window->flags; #endif - - if (flags & SDL_WINDOW_MODAL) { - SDL_SetWindowModalFor(window, parent); - } if (title) { SDL_SetWindowTitle(window, title); } @@ -2525,7 +2523,6 @@ bool SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags) bool need_vulkan_unload = false; bool need_vulkan_load = false; SDL_WindowFlags graphics_flags; - SDL_Window *parent = window->parent; // ensure no more than one of these flags is set graphics_flags = flags & (SDL_WINDOW_OPENGL | SDL_WINDOW_METAL | SDL_WINDOW_VULKAN); @@ -2552,7 +2549,7 @@ bool SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags) // If this is a modal dialog, clear the modal status. if (window->flags & SDL_WINDOW_MODAL) { - SDL_SetWindowModalFor(window, NULL); + SDL_SetWindowModal(window, false); } // Restore video mode, etc. @@ -2642,10 +2639,6 @@ bool SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags) window->flags |= SDL_WINDOW_EXTERNAL; } - if (flags & SDL_WINDOW_MODAL) { - SDL_SetWindowModalFor(window, parent); - } - if (_this->SetWindowTitle && window->title) { _this->SetWindowTitle(_this, window); } @@ -3579,40 +3572,59 @@ float SDL_GetWindowOpacity(SDL_Window *window) return window->opacity; } -SDL_bool SDL_SetWindowModalFor(SDL_Window *modal_window, SDL_Window *parent_window) +SDL_bool SDL_SetWindowParent(SDL_Window *window, SDL_Window *parent) { - bool result; + CHECK_WINDOW_MAGIC(window, false); + CHECK_WINDOW_NOT_POPUP(window, false); - CHECK_WINDOW_MAGIC(modal_window, false); - CHECK_WINDOW_NOT_POPUP(modal_window, false); - - if (parent_window) { - CHECK_WINDOW_MAGIC(parent_window, false); - CHECK_WINDOW_NOT_POPUP(parent_window, false); + if (parent) { + CHECK_WINDOW_MAGIC(parent, false); + CHECK_WINDOW_NOT_POPUP(parent, false); } - if (!_this->SetWindowModalFor) { + if (!_this->SetWindowParent) { return SDL_Unsupported(); } - if (parent_window) { - modal_window->flags |= SDL_WINDOW_MODAL; - } else if (modal_window->flags & SDL_WINDOW_MODAL) { - modal_window->flags &= ~SDL_WINDOW_MODAL; + if (window->flags & SDL_WINDOW_MODAL) { + return SDL_SetError("Modal windows cannot change parents; call SDL_SetWindowModal() to clear modal status first."); + } + + if (window->parent == parent) { + return true; + } + + const SDL_bool ret = _this->SetWindowParent(_this, window, parent); + SDL_UpdateWindowHierarchy(window, ret ? parent : NULL); + + return ret; +} + +SDL_bool SDL_SetWindowModal(SDL_Window *window, SDL_bool modal) +{ + CHECK_WINDOW_MAGIC(window, false); + CHECK_WINDOW_NOT_POPUP(window, false); + + if (!_this->SetWindowModal) { + return SDL_Unsupported(); + } + + if (modal) { + if (!window->parent) { + return SDL_SetError("Window must have a parent to enable the modal state; use SDL_SetWindowParent() to set the parent first."); + } + window->flags |= SDL_WINDOW_MODAL; + } else if (window->flags & SDL_WINDOW_MODAL) { + window->flags &= ~SDL_WINDOW_MODAL; } else { - return true; // Not modal; nothing to do. + return true; // Already not modal, so nothing to do. } - result = _this->SetWindowModalFor(_this, modal_window, parent_window); - - /* The existing parent might be needed when changing the modal status, - * so don't change the hierarchy until after setting the new modal state. - */ - if (result) { - SDL_SetWindowParent(modal_window, parent_window); + if (window->flags & SDL_WINDOW_HIDDEN) { + return true; } - return result; + return _this->SetWindowModal(_this, window, modal); } SDL_bool SDL_SetWindowFocusable(SDL_Window *window, SDL_bool focusable) @@ -4101,12 +4113,12 @@ void SDL_DestroyWindow(SDL_Window *window) SDL_DestroyProperties(window->text_input_props); SDL_DestroyProperties(window->props); - /* Clear the modal status, but don't unset the parent, as it may be - * needed later in the destruction process if a backend needs to - * update the input focus. + /* Clear the modal status, but don't unset the parent just yet, as it + * may be needed later in the destruction process if a backend needs + * to update the input focus. */ - if (_this->SetWindowModalFor && (window->flags & SDL_WINDOW_MODAL)) { - _this->SetWindowModalFor(_this, window, NULL); + if (_this->SetWindowModal && (window->flags & SDL_WINDOW_MODAL)) { + _this->SetWindowModal(_this, window, false); } // Make sure the destroyed window isn't referenced by any display as a fullscreen window. @@ -4168,9 +4180,9 @@ void SDL_DestroyWindow(SDL_Window *window) SDL_DestroySurface(window->icon); // Unlink the window from its siblings. - SDL_SetWindowParent(window, NULL); + SDL_UpdateWindowHierarchy(window, NULL); - // Unlink the window from the list + // Unlink the window from the global window list if (window->next) { window->next->prev = window->prev; } diff --git a/src/video/cocoa/SDL_cocoavideo.m b/src/video/cocoa/SDL_cocoavideo.m index 85795e1410..93b9153171 100644 --- a/src/video/cocoa/SDL_cocoavideo.m +++ b/src/video/cocoa/SDL_cocoavideo.m @@ -122,7 +122,8 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void) device->UpdateWindowShape = Cocoa_UpdateWindowShape; device->FlashWindow = Cocoa_FlashWindow; device->SetWindowFocusable = Cocoa_SetWindowFocusable; - device->SetWindowModalFor = Cocoa_SetWindowModalFor; + device->SetWindowParent = Cocoa_SetWindowParent; + device->SetWindowModal = Cocoa_SetWindowModal; device->SyncWindow = Cocoa_SyncWindow; #ifdef SDL_VIDEO_OPENGL_CGL diff --git a/src/video/cocoa/SDL_cocoawindow.h b/src/video/cocoa/SDL_cocoawindow.h index 3e0413871b..dd30c200a8 100644 --- a/src/video/cocoa/SDL_cocoawindow.h +++ b/src/video/cocoa/SDL_cocoawindow.h @@ -183,7 +183,8 @@ extern bool Cocoa_SetWindowHitTest(SDL_Window *window, bool enabled); extern void Cocoa_AcceptDragAndDrop(SDL_Window *window, bool accept); extern bool Cocoa_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation); extern bool Cocoa_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable); -extern bool Cocoa_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window); +extern bool Cocoa_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal); +extern bool Cocoa_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent); extern bool Cocoa_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window); #endif // SDL_cocoawindow_h_ diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m index ba12e02f69..21e437f5c0 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -2075,23 +2075,10 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, NSWindow window->flags &= ~SDL_WINDOW_MINIMIZED; } - if (!SDL_WINDOW_IS_POPUP(window)) { - if ([nswindow isKeyWindow]) { - window->flags |= SDL_WINDOW_INPUT_FOCUS; - Cocoa_SetKeyboardFocus(data.window); - } - } else { + if (window->parent) { NSWindow *nsparent = ((__bridge SDL_CocoaWindowData *)window->parent->internal).nswindow; [nsparent addChildWindow:nswindow ordered:NSWindowAbove]; - if (window->flags & SDL_WINDOW_TOOLTIP) { - [nswindow setIgnoresMouseEvents:YES]; - } else if (window->flags & SDL_WINDOW_POPUP_MENU) { - if (window->parent == SDL_GetKeyboardFocus()) { - Cocoa_SetKeyboardFocus(window); - } - } - /* FIXME: Should not need to call addChildWindow then orderOut. Attaching a hidden child window to a hidden parent window will cause the child window to show when the parent does. We therefore shouldn't attach the child window here as we're @@ -2103,6 +2090,21 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, NSWindow } } + if (!SDL_WINDOW_IS_POPUP(window)) { + if ([nswindow isKeyWindow]) { + window->flags |= SDL_WINDOW_INPUT_FOCUS; + Cocoa_SetKeyboardFocus(data.window); + } + } else { + if (window->flags & SDL_WINDOW_TOOLTIP) { + [nswindow setIgnoresMouseEvents:YES]; + } else if (window->flags & SDL_WINDOW_POPUP_MENU) { + if (window->parent == SDL_GetKeyboardFocus()) { + Cocoa_SetKeyboardFocus(window); + } + } + } + if (nswindow.isOpaque) { window->flags &= ~SDL_WINDOW_TRANSPARENT; } else { @@ -2465,14 +2467,15 @@ void Cocoa_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) if (![nswindow isMiniaturized]) { [windowData.listener pauseVisibleObservation]; - if (SDL_WINDOW_IS_POPUP(window)) { + if (window->parent) { NSWindow *nsparent = ((__bridge SDL_CocoaWindowData *)window->parent->internal).nswindow; [nsparent addChildWindow:nswindow ordered:NSWindowAbove]; - } else { - if ((window->flags & SDL_WINDOW_MODAL) && window->parent) { - Cocoa_SetWindowModalFor(_this, window, window->parent); - } + if (window->flags & SDL_WINDOW_MODAL) { + Cocoa_SetWindowModal(_this, window, true); + } + } + if (!SDL_WINDOW_IS_POPUP(window)) { if (bActivate) { [nswindow makeKeyAndOrderFront:nil]; } else { @@ -2482,9 +2485,9 @@ void Cocoa_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) } } } - [nswindow setIsVisible:YES]; - [windowData.listener resumeVisibleObservation]; } + [nswindow setIsVisible:YES]; + [windowData.listener resumeVisibleObservation]; } } @@ -2492,6 +2495,7 @@ void Cocoa_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) { @autoreleasepool { NSWindow *nswindow = ((__bridge SDL_CocoaWindowData *)window->internal).nswindow; + const BOOL waskey = [nswindow isKeyWindow]; /* orderOut has no effect on miniaturized windows, so close must be used to remove * the window from the desktop and window list in this case. @@ -2509,9 +2513,9 @@ void Cocoa_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) /* If this window is the source of a modal session, end it when * hidden, or other windows will be prevented from closing. */ - Cocoa_SetWindowModalFor(_this, window, NULL); + Cocoa_SetWindowModal(_this, window, false); - // Transfer keyboard focus back to the parent + // Transfer keyboard focus back to the parent when closing a popup menu if (window->flags & SDL_WINDOW_POPUP_MENU) { if (window == SDL_GetKeyboardFocus()) { SDL_Window *new_focus = window->parent; @@ -2523,6 +2527,20 @@ void Cocoa_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) Cocoa_SetKeyboardFocus(new_focus); } + } else if (window->parent && waskey) { + /* Key status is not automatically set on the parent when a child is hidden. Check if the + * child window was key, and set the first visible parent to be key if so. + */ + SDL_Window *new_focus = window->parent; + + while (new_focus->parent != NULL && (new_focus->is_hiding || new_focus->is_destroying)) { + new_focus = new_focus->parent; + } + + if (new_focus) { + NSWindow *newkey = ((__bridge SDL_CocoaWindowData *)window->internal).nswindow; + [newkey makeKeyAndOrderFront:nil]; + } } } } @@ -2539,19 +2557,21 @@ void Cocoa_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window) */ [windowData.listener pauseVisibleObservation]; if (![nswindow isMiniaturized] && [nswindow isVisible]) { - if (SDL_WINDOW_IS_POPUP(window)) { + if (window->parent) { NSWindow *nsparent = ((__bridge SDL_CocoaWindowData *)window->parent->internal).nswindow; [nsparent addChildWindow:nswindow ordered:NSWindowAbove]; - if (bActivate) { - [nswindow makeKeyWindow]; - } - } else { + } + if (!SDL_WINDOW_IS_POPUP(window)) { if (bActivate) { [NSApp activateIgnoringOtherApps:YES]; [nswindow makeKeyAndOrderFront:nil]; } else { [nswindow orderFront:nil]; } + } else { + if (bActivate) { + [nswindow makeKeyWindow]; + } } } [windowData.listener resumeVisibleObservation]; @@ -2943,7 +2963,7 @@ void Cocoa_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) */ if (topmost_data.keyboard_focus == window) { SDL_Window *new_focus = window; - while(new_focus->parent && (new_focus->is_hiding || new_focus->is_destroying)) { + while (new_focus->parent && (new_focus->is_hiding || new_focus->is_destroying)) { new_focus = new_focus->parent; } @@ -3054,18 +3074,38 @@ void Cocoa_AcceptDragAndDrop(SDL_Window *window, bool accept) } } -bool Cocoa_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window) +bool Cocoa_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent) { @autoreleasepool { - SDL_CocoaWindowData *modal_data = (__bridge SDL_CocoaWindowData *)modal_window->internal; + SDL_CocoaWindowData *child_data = (__bridge SDL_CocoaWindowData *)window->internal; - if (modal_data.modal_session) { - [NSApp endModalSession:modal_data.modal_session]; - modal_data.modal_session = nil; + // Remove an existing parent. + if (child_data.nswindow.parentWindow) { + NSWindow *nsparent = ((__bridge SDL_CocoaWindowData *)window->parent->internal).nswindow; + [nsparent removeChildWindow:child_data.nswindow]; } - if (parent_window) { - modal_data.modal_session = [NSApp beginModalSessionForWindow:modal_data.nswindow]; + if (parent) { + SDL_CocoaWindowData *parent_data = (__bridge SDL_CocoaWindowData *)parent->internal; + [parent_data.nswindow addChildWindow:child_data.nswindow ordered:NSWindowAbove]; + } + } + + return true; +} + +bool Cocoa_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal) +{ + @autoreleasepool { + SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal; + + if (data.modal_session) { + [NSApp endModalSession:data.modal_session]; + data.modal_session = nil; + } + + if (modal) { + data.modal_session = [NSApp beginModalSessionForWindow:data.nswindow]; } } diff --git a/src/video/haiku/SDL_bvideo.cc b/src/video/haiku/SDL_bvideo.cc index 942fc966c3..23521f73f2 100644 --- a/src/video/haiku/SDL_bvideo.cc +++ b/src/video/haiku/SDL_bvideo.cc @@ -82,6 +82,8 @@ static SDL_VideoDevice * HAIKU_CreateDevice(void) device->SetWindowFullscreen = HAIKU_SetWindowFullscreen; device->SetWindowMouseGrab = HAIKU_SetWindowMouseGrab; device->SetWindowMinimumSize = HAIKU_SetWindowMinimumSize; + device->SetWindowParent = HAIKU_SetWindowParent; + device->SetWindowModal = HAIKU_SetWindowModal; device->DestroyWindow = HAIKU_DestroyWindow; device->CreateWindowFramebuffer = HAIKU_CreateWindowFramebuffer; device->UpdateWindowFramebuffer = HAIKU_UpdateWindowFramebuffer; diff --git a/src/video/haiku/SDL_bwindow.cc b/src/video/haiku/SDL_bwindow.cc index b6eca9db3d..de9bc2484d 100644 --- a/src/video/haiku/SDL_bwindow.cc +++ b/src/video/haiku/SDL_bwindow.cc @@ -188,21 +188,22 @@ bool HAIKU_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window * window, bool return SDL_Unsupported(); } -bool HAIKU_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window) +bool HAIKU_SetWindowParent(SDL_VideoDevice *_this, SDL_Window * window, SDL_Window *parent) { - if (modal_window->parent && modal_window->parent != parent_window) { - // Remove from the subset of a previous parent. - _ToBeWin(modal_window)->RemoveFromSubset(_ToBeWin(modal_window->parent)); - } + return true; +} - if (parent_window) { - _ToBeWin(modal_window)->SetLook(B_MODAL_WINDOW_LOOK); - _ToBeWin(modal_window)->SetFeel(B_MODAL_SUBSET_WINDOW_FEEL); - _ToBeWin(modal_window)->AddToSubset(_ToBeWin(parent_window)); +bool HAIKU_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal) +{ + if (modal) { + _ToBeWin(window)->SetLook(B_MODAL_WINDOW_LOOK); + _ToBeWin(window)->SetFeel(B_MODAL_SUBSET_WINDOW_FEEL); + _ToBeWin(window)->AddToSubset(_ToBeWin(window->parent)); } else { - window_look look = (modal_window->flags & SDL_WINDOW_BORDERLESS) ? B_NO_BORDER_WINDOW_LOOK : B_TITLED_WINDOW_LOOK; - _ToBeWin(modal_window)->SetLook(look); - _ToBeWin(modal_window)->SetFeel(B_NORMAL_WINDOW_FEEL); + window_look look = (window->flags & SDL_WINDOW_BORDERLESS) ? B_NO_BORDER_WINDOW_LOOK : B_TITLED_WINDOW_LOOK; + _ToBeWin(window)->RemoveFromSubset(_ToBeWin(window->parent)); + _ToBeWin(window)->SetLook(look); + _ToBeWin(window)->SetFeel(B_NORMAL_WINDOW_FEEL); } return true; diff --git a/src/video/haiku/SDL_bwindow.h b/src/video/haiku/SDL_bwindow.h index 98c88d77fd..1bc711d146 100644 --- a/src/video/haiku/SDL_bwindow.h +++ b/src/video/haiku/SDL_bwindow.h @@ -39,6 +39,8 @@ extern void HAIKU_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, extern void HAIKU_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, bool resizable); extern SDL_FullscreenResult HAIKU_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen); extern bool HAIKU_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed); +extern bool HAIKU_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent); +extern bool HAIKU_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal); extern void HAIKU_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window); #endif diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index a3898c4da8..32cc022e7f 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -547,7 +547,8 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols) device->SetWindowSize = Wayland_SetWindowSize; device->SetWindowMinimumSize = Wayland_SetWindowMinimumSize; device->SetWindowMaximumSize = Wayland_SetWindowMaximumSize; - device->SetWindowModalFor = Wayland_SetWindowModalFor; + device->SetWindowParent = Wayland_SetWindowParent; + device->SetWindowModal = Wayland_SetWindowModal; device->SetWindowOpacity = Wayland_SetWindowOpacity; device->SetWindowTitle = Wayland_SetWindowTitle; device->SetWindowIcon = Wayland_SetWindowIcon; diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index 4596a3078e..24ee3eefde 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -672,8 +672,11 @@ static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time for (SDL_Window *w = wind->sdlwindow->first_child; w; w = w->next_sibling) { if (w->internal->surface_status == WAYLAND_SURFACE_STATUS_SHOW_PENDING) { Wayland_ShowWindow(SDL_GetVideoDevice(), w); - } else if ((w->flags & SDL_WINDOW_MODAL) && w->internal->modal_reparenting_required) { - Wayland_SetWindowModalFor(SDL_GetVideoDevice(), w, w->parent); + } else if (w->internal->reparenting_required) { + Wayland_SetWindowParent(SDL_GetVideoDevice(), w, w->parent); + if (w->flags & SDL_WINDOW_MODAL) { + Wayland_SetWindowModal(SDL_GetVideoDevice(), w, true); + } } } @@ -1564,58 +1567,75 @@ bool Wayland_SetWindowHitTest(SDL_Window *window, bool enabled) return true; // just succeed, the real work is done elsewhere. } -bool Wayland_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window) +static struct xdg_toplevel *GetToplevelForWindow(SDL_WindowData *wind) { - SDL_VideoData *viddata = _this->internal; - SDL_WindowData *modal_data = modal_window->internal; - SDL_WindowData *parent_data = parent_window ? parent_window->internal : NULL; - struct xdg_toplevel *modal_toplevel = NULL; - struct xdg_toplevel *parent_toplevel = NULL; + if (wind) { + /* Libdecor crashes on attempts to unset the parent by passing null, which is allowed by the + * toplevel spec, so just use the raw xdg-toplevel instead (that's what libdecor does + * internally anyways). + */ +#ifdef HAVE_LIBDECOR_H + if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR && wind->shell_surface.libdecor.frame) { + return libdecor_frame_get_xdg_toplevel(wind->shell_surface.libdecor.frame); + } else +#endif + if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL && wind->shell_surface.xdg.roleobj.toplevel) { + return wind->shell_surface.xdg.roleobj.toplevel; + } + } - modal_data->modal_reparenting_required = false; + return NULL; +} + +bool Wayland_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent_window) +{ + SDL_WindowData *child_data = window->internal; + SDL_WindowData *parent_data = parent_window ? parent_window->internal : NULL; + + child_data->reparenting_required = SDL_FALSE; if (parent_data && parent_data->surface_status != WAYLAND_SURFACE_STATUS_SHOWN) { // Need to wait for the parent to become mapped, or it's the same as setting a null parent. - modal_data->modal_reparenting_required = true; + child_data->reparenting_required = SDL_TRUE; return true; } - /* Libdecor crashes on attempts to unset the parent by passing null, which is allowed by the - * toplevel spec, so just use the raw xdg-toplevel instead (that's what libdecor does - * internally anyways). - */ -#ifdef HAVE_LIBDECOR_H - if (modal_data->shell_surface_type == WAYLAND_SURFACE_LIBDECOR && modal_data->shell_surface.libdecor.frame) { - modal_toplevel = libdecor_frame_get_xdg_toplevel(modal_data->shell_surface.libdecor.frame); - } else -#endif - if (modal_data->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL && modal_data->shell_surface.xdg.roleobj.toplevel) { - modal_toplevel = modal_data->shell_surface.xdg.roleobj.toplevel; + struct xdg_toplevel *child_toplevel = GetToplevelForWindow(child_data); + struct xdg_toplevel *parent_toplevel = GetToplevelForWindow(parent_data); + + if (child_toplevel) { + xdg_toplevel_set_parent(child_toplevel, parent_toplevel); } - if (parent_data) { -#ifdef HAVE_LIBDECOR_H - if (parent_data->shell_surface_type == WAYLAND_SURFACE_LIBDECOR && parent_data->shell_surface.libdecor.frame) { - parent_toplevel = libdecor_frame_get_xdg_toplevel(parent_data->shell_surface.libdecor.frame); - } else -#endif - if (parent_data->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL && parent_data->shell_surface.xdg.roleobj.toplevel) { - parent_toplevel = parent_data->shell_surface.xdg.roleobj.toplevel; - } + return true; +} + +bool Wayland_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal) +{ + SDL_VideoData *viddata = _this->internal; + SDL_WindowData *data = window->internal; + SDL_WindowData *parent_data = window->parent->internal; + + if (parent_data->surface_status != WAYLAND_SURFACE_STATUS_SHOWN) { + // Need to wait for the parent to become mapped before changing modal status. + data->reparenting_required = true; + return true; + } else { + data->reparenting_required = false; } - if (modal_toplevel) { - xdg_toplevel_set_parent(modal_toplevel, parent_toplevel); + struct xdg_toplevel *toplevel = GetToplevelForWindow(data); + if (toplevel) { if (viddata->xdg_wm_dialog_v1) { - if (parent_toplevel) { - if (!modal_data->xdg_dialog_v1) { - modal_data->xdg_dialog_v1 = xdg_wm_dialog_v1_get_xdg_dialog(viddata->xdg_wm_dialog_v1, modal_toplevel); + if (modal) { + if (!data->xdg_dialog_v1) { + data->xdg_dialog_v1 = xdg_wm_dialog_v1_get_xdg_dialog(viddata->xdg_wm_dialog_v1, toplevel); } - xdg_dialog_v1_set_modal(modal_data->xdg_dialog_v1); - } else if (modal_data->xdg_dialog_v1) { - xdg_dialog_v1_unset_modal(modal_data->xdg_dialog_v1); + xdg_dialog_v1_set_modal(data->xdg_dialog_v1); + } else if (data->xdg_dialog_v1) { + xdg_dialog_v1_unset_modal(data->xdg_dialog_v1); } } } @@ -1820,8 +1840,10 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) } // Restore state that was set prior to this call + Wayland_SetWindowParent(_this, window, window->parent); + if (window->flags & SDL_WINDOW_MODAL) { - Wayland_SetWindowModalFor(_this, window, window->parent); + Wayland_SetWindowModal(_this, window, true); } Wayland_SetWindowTitle(_this, window); diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index 278884f2df..344ac583f9 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -172,7 +172,7 @@ struct SDL_WindowData bool fullscreen_was_positioned; bool show_hide_sync_required; bool scale_to_display; - bool modal_reparenting_required; + bool reparenting_required; bool pending_restored_size; bool double_buffer; @@ -200,7 +200,8 @@ extern void Wayland_SetWindowMinimumSize(SDL_VideoDevice *_this, SDL_Window *win extern void Wayland_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window); extern void Wayland_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h); extern SDL_DisplayID Wayland_GetDisplayForWindow(SDL_VideoDevice *_this, SDL_Window *window); -extern bool Wayland_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window); +extern bool Wayland_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent_window); +extern bool Wayland_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal); extern bool Wayland_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity); extern void Wayland_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window); extern void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y); diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index 05b45bc93f..b9e3a99612 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -229,7 +229,8 @@ static SDL_VideoDevice *WIN_CreateDevice(void) device->SetWindowResizable = WIN_SetWindowResizable; device->SetWindowAlwaysOnTop = WIN_SetWindowAlwaysOnTop; device->SetWindowFullscreen = WIN_SetWindowFullscreen; - device->SetWindowModalFor = WIN_SetWindowModalFor; + device->SetWindowParent = WIN_SetWindowParent; + device->SetWindowModal = WIN_SetWindowModal; #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) device->GetWindowICCProfile = WIN_GetWindowICCProfile; device->SetWindowMouseRect = WIN_SetWindowMouseRect; diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index bbe8aa8b81..9d1d820c49 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -693,10 +693,10 @@ bool WIN_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properties int x, y; int w, h; - if (SDL_WINDOW_IS_POPUP(window)) { - parent = window->parent->internal->hwnd; - } else if (window->flags & SDL_WINDOW_UTILITY) { + if (window->flags & SDL_WINDOW_UTILITY) { parent = CreateWindow(SDL_Appname, TEXT(""), STYLE_BASIC, 0, 0, 32, 32, NULL, NULL, SDL_Instance, NULL); + } else if (window->parent) { + parent = window->parent->internal->hwnd; } style |= GetWindowStyle(window); @@ -1017,12 +1017,6 @@ void WIN_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) WIN_SetWindowPosition(_this, window); } -#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) - if (window->flags & SDL_WINDOW_MODAL) { - EnableWindow(window->parent->internal->hwnd, FALSE); - } -#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) - hwnd = window->internal->hwnd; style = GetWindowLong(hwnd, GWL_EXSTYLE); if (style & WS_EX_NOACTIVATE) { @@ -1040,17 +1034,18 @@ void WIN_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) WIN_SetKeyboardFocus(window); } } + if (window->flags & SDL_WINDOW_MODAL) { + WIN_SetWindowModal(_this, window, true); + } } void WIN_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) { HWND hwnd = window->internal->hwnd; -#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) if (window->flags & SDL_WINDOW_MODAL) { - EnableWindow(window->parent->internal->hwnd, TRUE); + WIN_SetWindowModal(_this, window, false); } -#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) ShowWindow(hwnd, SW_HIDE); @@ -2240,22 +2235,12 @@ void WIN_UpdateDarkModeForHWND(HWND hwnd) } } -bool WIN_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window) +bool WIN_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent) { #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) - SDL_WindowData *modal_data = modal_window->internal; - const LONG_PTR parent_hwnd = (LONG_PTR)(parent_window ? parent_window->internal->hwnd : NULL); - const LONG_PTR old_ptr = GetWindowLongPtr(modal_data->hwnd, GWLP_HWNDPARENT); - const DWORD style = GetWindowLong(modal_data->hwnd, GWL_STYLE); - - if (old_ptr == parent_hwnd) { - return true; - } - - // Reenable the old parent window. - if (old_ptr) { - EnableWindow((HWND)old_ptr, TRUE); - } + SDL_WindowData *child_data = window->internal; + const LONG_PTR parent_hwnd = (LONG_PTR)(parent ? parent->internal->hwnd : NULL); + const DWORD style = GetWindowLong(child_data->hwnd, GWL_STYLE); if (!(style & WS_CHILD)) { /* Despite the name, this changes the *owner* of a toplevel window, not @@ -2263,14 +2248,26 @@ bool WIN_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL * * https://devblogs.microsoft.com/oldnewthing/20100315-00/?p=14613 */ - SetWindowLongPtr(modal_data->hwnd, GWLP_HWNDPARENT, parent_hwnd); + SetWindowLongPtr(child_data->hwnd, GWLP_HWNDPARENT, parent_hwnd); } else { - SetParent(modal_data->hwnd, (HWND)parent_hwnd); + SetParent(child_data->hwnd, (HWND)parent_hwnd); } +#endif /*!defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)*/ - // Disable the new parent window if the modal window is visible. - if (!(modal_window->flags & SDL_WINDOW_HIDDEN) && parent_hwnd) { - EnableWindow((HWND)parent_hwnd, FALSE); + return true; +} + +bool WIN_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal) +{ +#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) + const HWND parent_hwnd = window->parent->internal->hwnd; + + if (modal) { + // Disable the parent window. + EnableWindow(parent_hwnd, FALSE); + } else if (!(window->flags & SDL_WINDOW_HIDDEN)) { + // Re-enable the parent window + EnableWindow(parent_hwnd, TRUE); } #endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h index 84b57dc9d1..9a448a7971 100644 --- a/src/video/windows/SDL_windowswindow.h +++ b/src/video/windows/SDL_windowswindow.h @@ -137,7 +137,8 @@ extern void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y); extern bool WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable); extern bool WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width, int *height, SDL_WindowRect rect_type); extern bool WIN_AdjustWindowRectForHWND(HWND hwnd, LPRECT lpRect, UINT frame_dpi); -extern bool WIN_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window); +extern bool WIN_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent); +extern bool WIN_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal); // Ends C function definitions when using C++ #ifdef __cplusplus diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c index 39eba0ff9f..04a811de74 100644 --- a/src/video/x11/SDL_x11video.c +++ b/src/video/x11/SDL_x11video.c @@ -197,7 +197,8 @@ static SDL_VideoDevice *X11_CreateDevice(void) device->SetWindowAspectRatio = X11_SetWindowAspectRatio; device->GetWindowBordersSize = X11_GetWindowBordersSize; device->SetWindowOpacity = X11_SetWindowOpacity; - device->SetWindowModalFor = X11_SetWindowModalFor; + device->SetWindowParent = X11_SetWindowParent; + device->SetWindowModal = X11_SetWindowModal; device->ShowWindow = X11_ShowWindow; device->HideWindow = X11_HideWindow; device->RaiseWindow = X11_RaiseWindow; diff --git a/src/video/x11/SDL_x11window.c b/src/video/x11/SDL_x11window.c index 1989144927..509fa08c04 100644 --- a/src/video/x11/SDL_x11window.c +++ b/src/video/x11/SDL_x11window.c @@ -792,6 +792,11 @@ bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properties } windowdata = window->internal; + // Set the parent if this is a non-popup window. + if (!SDL_WINDOW_IS_POPUP(window) && window->parent) { + X11_XSetTransientForHint(display, w, window->parent->internal->xwindow); + } + // Set the flag if the borders were forced on when creating a fullscreen window for later removal. windowdata->fullscreen_borders_forced_on = !!(window->pending_flags & SDL_WINDOW_FULLSCREEN) && !!(window->flags & SDL_WINDOW_BORDERLESS); @@ -1235,26 +1240,40 @@ bool X11_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opac return true; } -bool X11_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window) +bool X11_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent) { - SDL_WindowData *data = modal_window->internal; - SDL_WindowData *parent_data = parent_window ? parent_window->internal : NULL; + SDL_WindowData *data = window->internal; + SDL_WindowData *parent_data = parent ? parent->internal : NULL; SDL_VideoData *video_data = _this->internal; - SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(modal_window); Display *display = video_data->display; - Uint32 flags = modal_window->flags; + + if (parent_data) { + X11_XSetTransientForHint(display, data->xwindow, parent_data->xwindow); + } else { + X11_XDeleteProperty(display, data->xwindow, video_data->WM_TRANSIENT_FOR); + } + + return true; +} + +bool X11_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal) +{ + SDL_WindowData *data = window->internal; + SDL_VideoData *video_data = _this->internal; + SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); + Display *display = video_data->display; + Uint32 flags = window->flags; Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE; Atom _NET_WM_STATE_MODAL = data->videodata->_NET_WM_STATE_MODAL; - if (parent_data) { + if (modal) { flags |= SDL_WINDOW_MODAL; - X11_XSetTransientForHint(display, data->xwindow, parent_data->xwindow); } else { flags &= ~SDL_WINDOW_MODAL; X11_XDeleteProperty(display, data->xwindow, video_data->WM_TRANSIENT_FOR); } - if (X11_IsWindowMapped(_this, modal_window)) { + if (X11_IsWindowMapped(_this, window)) { XEvent e; SDL_zero(e); @@ -1262,8 +1281,7 @@ bool X11_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL e.xclient.message_type = _NET_WM_STATE; e.xclient.format = 32; e.xclient.window = data->xwindow; - e.xclient.data.l[0] = - parent_data ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + e.xclient.data.l[0] = modal ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; e.xclient.data.l[1] = _NET_WM_STATE_MODAL; e.xclient.data.l[3] = 0l; diff --git a/src/video/x11/SDL_x11window.h b/src/video/x11/SDL_x11window.h index 4bf7e8a208..d867255ba7 100644 --- a/src/video/x11/SDL_x11window.h +++ b/src/video/x11/SDL_x11window.h @@ -118,7 +118,8 @@ extern void X11_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window) extern void X11_SetWindowAspectRatio(SDL_VideoDevice *_this, SDL_Window *window); extern bool X11_GetWindowBordersSize(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right); extern bool X11_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity); -extern bool X11_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window); +extern bool X11_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent); +extern bool X11_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal); extern void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window); extern void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window); extern void X11_HideWindow(SDL_VideoDevice *_this, SDL_Window *window); diff --git a/test/testmodal.c b/test/testmodal.c index 608d9176e0..4fa9c1abd1 100644 --- a/test/testmodal.c +++ b/test/testmodal.c @@ -66,8 +66,10 @@ int main(int argc, char *argv[]) goto sdl_quit; } - if (!SDL_SetWindowModalFor(w2, w1)) { - SDL_SetWindowTitle(w2, "Modal Window"); + if (SDL_SetWindowParent(w2, w1)) { + if (SDL_SetWindowModal(w2, true)) { + SDL_SetWindowTitle(w2, "Modal Window"); + } } while (1) { @@ -98,8 +100,10 @@ int main(int argc, char *argv[]) } if (e.key.key == SDLK_M) { - if (!SDL_SetWindowModalFor(w2, w1)) { - SDL_SetWindowTitle(w2, "Modal Window"); + if (SDL_SetWindowParent(w2, w2)) { + if (SDL_SetWindowModal(w2, true)) { + SDL_SetWindowTitle(w2, "Modal Window"); + } } } SDL_ShowWindow(w2); @@ -123,13 +127,17 @@ int main(int argc, char *argv[]) } else if (e.key.key == SDLK_P && w2) { if (SDL_GetWindowFlags(w2) & SDL_WINDOW_MODAL) { /* Unparent the window */ - if (!SDL_SetWindowModalFor(w2, NULL)) { - SDL_SetWindowTitle(w2, "Non-Modal Window"); + if (SDL_SetWindowModal(w2, false)) { + if (SDL_SetWindowParent(w2, NULL)) { + SDL_SetWindowTitle(w2, "Non-Modal Window"); + } } } else { /* Reparent the window */ - if (!SDL_SetWindowModalFor(w2, w1)) { - SDL_SetWindowTitle(w2, "Modal Window"); + if (SDL_SetWindowParent(w2, w1)) { + if (SDL_SetWindowModal(w2, true)) { + SDL_SetWindowTitle(w2, "Modal Window"); + } } } }