diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h index fe68a5827..a565af703 100644 --- a/include/SDL3/SDL_video.h +++ b/include/SDL3/SDL_video.h @@ -1529,13 +1529,12 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowIcon(SDL_Window *window, SDL_Surfa /** * Request that the window's position be set. * - * If, at the time of this request, the window is in a fixed-size state such - * as maximized, this request may be deferred until the window returns to a - * resizable state. + * If the window is in an exclusive fullscreen or maximized state, this request + * has no effect. * * This can be used to reposition fullscreen-desktop windows onto a different - * display, however, exclusive fullscreen windows are locked to a specific - * display and can only be repositioned programmatically via + * display, however, as exclusive fullscreen windows are locked to a specific + * display, they can only be repositioned programmatically via * SDL_SetWindowFullscreenMode(). * * On some windowing systems this request is asynchronous and the new @@ -1596,12 +1595,11 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowPosition(SDL_Window *window, int * /** * Request that the size of a window's client area be set. * - * If, at the time of this request, the window in a fixed-size state, such as - * maximized or fullscreen, the request will be deferred until the window - * exits this state and becomes resizable again. + * If the window is in a fullscreen or maximized state, this request has no + * effect. * - * To change the fullscreen mode of a window, use - * SDL_SetWindowFullscreenMode() + * To change the exclusive fullscreen mode of a window, use + * SDL_SetWindowFullscreenMode(). * * On some windowing systems, this request is asynchronous and the new window * size may not have have been applied immediately upon the return of this @@ -2021,6 +2019,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_MaximizeWindow(SDL_Window *window); /** * Request that the window be minimized to an iconic representation. * + * If the window is in a fullscreen state, this request has no direct effect. + * It may alter the state the window is returned to when leaving fullscreen. + * * On some windowing systems this request is asynchronous and the new window * state may not have been applied immediately upon the return of this * function. If an immediate change is required, call SDL_SyncWindow() to @@ -2048,6 +2049,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_MinimizeWindow(SDL_Window *window); * Request that the size and position of a minimized or maximized window be * restored. * + * If the window is in a fullscreen state, this request has no direct effect. + * It may alter the state the window is returned to when leaving fullscreen. + * * On some windowing systems this request is asynchronous and the new window * state may not have have been applied immediately upon the return of this * function. If an immediate change is required, call SDL_SyncWindow() to diff --git a/src/events/SDL_windowevents.c b/src/events/SDL_windowevents.c index b31f3edd0..5535ec505 100644 --- a/src/events/SDL_windowevents.c +++ b/src/events/SDL_windowevents.c @@ -69,7 +69,7 @@ bool SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent, int data case SDL_EVENT_WINDOW_MOVED: window->undefined_x = false; window->undefined_y = false; - window->use_pending_position_for_fullscreen = false; + window->last_position_pending = false; if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { window->windowed.x = data1; window->windowed.y = data2; @@ -86,6 +86,7 @@ bool SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent, int data window->y = data2; break; case SDL_EVENT_WINDOW_RESIZED: + window->last_size_pending = false; if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { window->windowed.w = data1; window->windowed.h = data2; diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index cd63a18d2..51d991f1d 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -78,6 +78,9 @@ struct SDL_Window */ SDL_Rect floating; + // The last client requested size and position for the window. + SDL_Rect pending; + /* Toggle for drivers to indicate that the current window state is tiled, * and sizes set non-programmatically shouldn't be cached. */ @@ -98,7 +101,8 @@ struct SDL_Window bool is_hiding; bool restore_on_show; // Child was hidden recursively by the parent, restore when shown. - bool use_pending_position_for_fullscreen; + bool last_position_pending; // This should NOT be cleared by the backend, as it is used for fullscreen positioning. + bool last_size_pending; // This should be cleared by the backend if the new size cannot be applied. bool is_destroying; bool is_dropping; // drag/drop in progress, expecting SDL_SendDropComplete(). diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 9760a4426..8aebf769b 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -1664,14 +1664,13 @@ SDL_VideoDisplay *SDL_GetVideoDisplayForFullscreenWindow(SDL_Window *window) * the current position won't be updated at the time of the fullscreen call. */ if (!displayID) { - if (window->use_pending_position_for_fullscreen) { - // The last coordinates were client requested; use the pending floating coordinates. - displayID = GetDisplayForRect(window->floating.x, window->floating.y, window->floating.w, window->floating.h); - } - else { - // The last coordinates were from the window manager; use the current position. - displayID = GetDisplayForRect(window->x, window->y, 1, 1); - } + // Use the pending position and dimensions, if available, otherwise, use the current. + const int x = window->last_position_pending ? window->pending.x : window->x; + const int y = window->last_position_pending ? window->pending.y : window->y; + const int w = window->last_size_pending ? window->pending.w : window->w; + const int h = window->last_size_pending ? window->pending.h : window->h; + + displayID = GetDisplayForRect(x, y, w, h); } if (!displayID) { // Use the primary display for a window if we can't find it anywhere else @@ -2763,6 +2762,9 @@ bool SDL_SetWindowPosition(SDL_Window *window, int x, int y) CHECK_WINDOW_MAGIC(window, false); + const int w = window->last_size_pending ? window->pending.w : window->windowed.w; + const int h = window->last_size_pending ? window->pending.h : window->windowed.h; + original_displayID = SDL_GetDisplayForWindow(window); if (SDL_WINDOWPOS_ISUNDEFINED(x)) { @@ -2785,26 +2787,24 @@ bool SDL_SetWindowPosition(SDL_Window *window, int x, int y) } SDL_zero(bounds); - if (!SDL_GetDisplayUsableBounds(displayID, &bounds) || - window->windowed.w > bounds.w || - window->windowed.h > bounds.h) { + if (!SDL_GetDisplayUsableBounds(displayID, &bounds) || w > bounds.w || h > bounds.h) { if (!SDL_GetDisplayBounds(displayID, &bounds)) { return false; } } if (SDL_WINDOWPOS_ISCENTERED(x)) { - x = bounds.x + (bounds.w - window->windowed.w) / 2; + x = bounds.x + (bounds.w - w) / 2; } if (SDL_WINDOWPOS_ISCENTERED(y)) { - y = bounds.y + (bounds.h - window->windowed.h) / 2; + y = bounds.y + (bounds.h - h) / 2; } } - window->floating.x = x; - window->floating.y = y; + window->pending.x = x; + window->pending.y = y; window->undefined_x = false; window->undefined_y = false; - window->use_pending_position_for_fullscreen = true; + window->last_position_pending = true; if (_this->SetWindowPosition) { const bool result = _this->SetWindowPosition(_this, window); @@ -2952,8 +2952,9 @@ bool SDL_SetWindowSize(SDL_Window *window, int w, int h) h = window->max_h; } - window->floating.w = w; - window->floating.h = h; + window->last_size_pending = true; + window->pending.w = w; + window->pending.h = h; if (_this->SetWindowSize) { _this->SetWindowSize(_this, window); diff --git a/src/video/cocoa/SDL_cocoawindow.h b/src/video/cocoa/SDL_cocoawindow.h index dd30c200a..9add0c0e5 100644 --- a/src/video/cocoa/SDL_cocoawindow.h +++ b/src/video/cocoa/SDL_cocoawindow.h @@ -38,7 +38,8 @@ typedef enum PENDING_OPERATION_NONE = 0x00, PENDING_OPERATION_ENTER_FULLSCREEN = 0x01, PENDING_OPERATION_LEAVE_FULLSCREEN = 0x02, - PENDING_OPERATION_MINIMIZE = 0x04 + PENDING_OPERATION_MINIMIZE = 0x04, + PENDING_OPERATION_ZOOM = 0x08 } PendingWindowOperation; @interface SDL3Cocoa_WindowListener : NSResponder @@ -144,10 +145,10 @@ typedef enum @property(nonatomic) SDL3Cocoa_WindowListener *listener; @property(nonatomic) NSModalSession modal_session; @property(nonatomic) SDL_CocoaVideoData *videodata; -@property(nonatomic) bool send_floating_size; -@property(nonatomic) bool send_floating_position; +@property(nonatomic) bool pending_size; +@property(nonatomic) bool pending_position; @property(nonatomic) bool border_toggled; -@property(nonatomic) BOOL checking_zoom; + #ifdef SDL_VIDEO_OPENGL_EGL @property(nonatomic) EGLSurface egl_surface; #endif diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m index f2238f015..0401a0453 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -666,17 +666,6 @@ static void Cocoa_WaitForMiniaturizable(SDL_Window *window) } } -static bool Cocoa_IsZoomed(SDL_Window *window) -{ - SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal; - - data.checking_zoom = YES; - const bool ret = [data.nswindow isZoomed]; - data.checking_zoom = NO; - - return ret; -} - static NSCursor *Cocoa_GetDesiredCursor(void) { SDL_Mouse *mouse = SDL_GetMouse(); @@ -1043,27 +1032,6 @@ static NSCursor *Cocoa_GetDesiredCursor(void) { SDL_Window *window = _data.window; - /* XXX: Calling [isZoomed] calls this function, and calling [isZoomed] - * from within this function will recurse until the stack overflows, - * so a recursion guard is required. - */ - if (!_data.checking_zoom) { - _data.checking_zoom = YES; - if ([_data.nswindow isZoomed] && !_data.was_zoomed && _data.send_floating_size) { - NSRect rect; - - _data.send_floating_size = NO; - rect.origin.x = window->floating.x; - rect.origin.y = window->floating.y; - rect.size.width = window->floating.w; - rect.size.height = window->floating.h; - ConvertNSRect(&rect); - - frameSize = rect.size; - } - _data.checking_zoom = NO; - } - if (window->min_aspect > 0.0f || window->max_aspect > 0.0f) { NSWindow *nswindow = _data.nswindow; NSRect newContentRect = [nswindow contentRectForFrameRect:NSMakeRect(0, 0, frameSize.width, frameSize.height)]; @@ -1119,7 +1087,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void) /* isZoomed always returns true if the window is not resizable * and fullscreen windows are considered zoomed. */ - if ((window->flags & SDL_WINDOW_RESIZABLE) && Cocoa_IsZoomed(window) && + if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed] && !(window->flags & SDL_WINDOW_FULLSCREEN) && ![self isInFullscreenSpace]) { zoomed = YES; } else { @@ -1164,7 +1132,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void) SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_RESTORED, 0, 0); // isZoomed always returns true if the window is not resizable. - if ((_data.window->flags & SDL_WINDOW_RESIZABLE) && Cocoa_IsZoomed(_data.window)) { + if ((_data.window->flags & SDL_WINDOW_RESIZABLE) && [_data.nswindow isZoomed]) { SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0); } @@ -1324,6 +1292,9 @@ static NSCursor *Cocoa_GetDesiredCursor(void) } SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0); + _data.pending_position = NO; + _data.pending_size = NO; + /* Force the size change event in case it was delivered earlier while the window was still animating into place. */ @@ -1362,6 +1333,11 @@ static NSCursor *Cocoa_GetDesiredCursor(void) return; } + _data.pending_position = NO; + _data.pending_size = NO; + window->last_position_pending = false; + window->last_size_pending = false; + SetWindowStyle(window, flags); isFullscreenSpace = YES; @@ -1425,18 +1401,32 @@ static NSCursor *Cocoa_GetDesiredCursor(void) } [NSMenu setMenuBarVisible:YES]; - // Restore windowed size and position in case it changed while fullscreen - NSRect rect; - rect.origin.x = _data.was_zoomed ? window->windowed.x : window->floating.x; - rect.origin.y = _data.was_zoomed ? window->windowed.y : window->floating.y; - rect.size.width = _data.was_zoomed ? window->windowed.w : window->floating.w; - rect.size.height = _data.was_zoomed ? window->windowed.h : window->floating.h; - ConvertNSRect(&rect); + // Toggle zoom, if changed while fullscreen. + if ([self windowOperationIsPending:PENDING_OPERATION_ZOOM]) { + [self clearPendingWindowOperation:PENDING_OPERATION_ZOOM]; + [nswindow zoom:nil]; + } - _data.send_floating_position = NO; - _data.send_floating_size = NO; - [nswindow setContentSize:rect.size]; - [nswindow setFrameOrigin:rect.origin]; + if (![nswindow isZoomed]) { + // Apply a pending window size, if not zoomed. + NSRect rect; + rect.origin.x = _data.pending_position ? window->pending.x : window->floating.x; + rect.origin.y = _data.pending_position ? window->pending.y : window->floating.y; + rect.size.width = _data.pending_size ? window->pending.w : window->floating.w; + rect.size.height = _data.pending_size ? window->pending.h : window->floating.h; + ConvertNSRect(&rect); + + if (_data.pending_size) { + [nswindow setContentSize:rect.size]; + } + if (_data.pending_position) { + [nswindow setFrameOrigin:rect.origin]; + } + } + + _data.pending_size = NO; + _data.pending_position = NO; + _data.was_zoomed = NO; /* Force the size change event in case it was delivered earlier * while the window was still animating into place. @@ -1446,8 +1436,6 @@ static NSCursor *Cocoa_GetDesiredCursor(void) [self windowDidMove:aNotification]; [self windowDidResize:aNotification]; - _data.was_zoomed = false; - // FIXME: Why does the window get hidden? if (!(window->flags & SDL_WINDOW_HIDDEN)) { Cocoa_ShowWindow(SDL_GetVideoDevice(), window); @@ -2083,7 +2071,7 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, NSWindow } // isZoomed always returns true if the window is not resizable - if ((window->flags & SDL_WINDOW_RESIZABLE) && Cocoa_IsZoomed(window)) { + if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) { window->flags |= SDL_WINDOW_MAXIMIZED; } else { window->flags &= ~SDL_WINDOW_MAXIMIZED; @@ -2352,9 +2340,9 @@ bool Cocoa_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) BOOL fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO; int x, y; - if ([windata.listener windowOperationIsPending:(PENDING_OPERATION_ENTER_FULLSCREEN | PENDING_OPERATION_LEAVE_FULLSCREEN)] || - [windata.listener isInFullscreenSpaceTransition]) { - Cocoa_SyncWindow(_this, window); + if ([windata.listener isInFullscreenSpaceTransition]) { + windata.pending_position = YES; + return true; } if (!(window->flags & SDL_WINDOW_MAXIMIZED)) { @@ -2366,7 +2354,7 @@ bool Cocoa_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) rect.origin.x = r.x; rect.origin.y = r.y; } else { - SDL_RelativeToGlobalForWindow(window, window->floating.x, window->floating.y, &x, &y); + SDL_RelativeToGlobalForWindow(window, window->pending.x, window->pending.y, &x, &y); rect.origin.x = x; rect.origin.y = y; } @@ -2389,8 +2377,6 @@ bool Cocoa_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) [nswindow setFrameOrigin:rect.origin]; ScheduleContextUpdates(windata); - } else { - windata.send_floating_position = true; } } return true; @@ -2402,13 +2388,13 @@ void Cocoa_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) SDL_CocoaWindowData *windata = (__bridge SDL_CocoaWindowData *)window->internal; NSWindow *nswindow = windata.nswindow; - if ([windata.listener windowOperationIsPending:(PENDING_OPERATION_ENTER_FULLSCREEN | PENDING_OPERATION_LEAVE_FULLSCREEN)] || - [windata.listener isInFullscreenSpaceTransition]) { - Cocoa_SyncWindow(_this, window); + if ([windata.listener isInFullscreenSpaceTransition]) { + windata.pending_size = YES; + return; } // isZoomed always returns true if the window is not resizable - if (!(window->flags & SDL_WINDOW_RESIZABLE) || !Cocoa_IsZoomed(window)) { + if (!(window->flags & SDL_WINDOW_RESIZABLE) || ![nswindow isZoomed]) { if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { int x, y; NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]]; @@ -2420,17 +2406,19 @@ void Cocoa_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) SDL_RelativeToGlobalForWindow(window, window->floating.x, window->floating.y, &x, &y); rect.origin.x = x; rect.origin.y = y; - rect.size.width = window->floating.w; - rect.size.height = window->floating.h; + rect.size.width = window->pending.w; + rect.size.height = window->pending.h; ConvertNSRect(&rect); [nswindow setFrame:[nswindow frameRectForContentRect:rect] display:YES]; ScheduleContextUpdates(windata); - } else if (windata.was_zoomed) { - windata.send_floating_size = true; + } else { + // Can't set the window size. + window->last_size_pending = false; } - } else { - windata.send_floating_size = true; + } else { + // Can't set the window size. + window->last_size_pending = false; } } } @@ -2614,6 +2602,10 @@ void Cocoa_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) ![windata.listener isInFullscreenSpace]) { [nswindow zoom:nil]; ScheduleContextUpdates(windata); + } else if (!windata.was_zoomed) { + [windata.listener addPendingWindowOperation:PENDING_OPERATION_ZOOM]; + } else { + [windata.listener clearPendingWindowOperation:PENDING_OPERATION_ZOOM]; } } } @@ -2654,27 +2646,13 @@ void Cocoa_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) ![data.listener isInFullscreenSpace]) { if ([nswindow isMiniaturized]) { [nswindow deminiaturize:nil]; - } else if ((window->flags & SDL_WINDOW_RESIZABLE) && Cocoa_IsZoomed(window)) { - NSRect rect; - - // Update the floating coordinates - rect.origin.x = window->floating.x; - rect.origin.y = window->floating.y; - - // The floating size will be set in windowWillResize + } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [data.nswindow isZoomed]) { [nswindow zoom:nil]; - - rect.size.width = window->floating.w; - rect.size.height = window->floating.h; - - ConvertNSRect(&rect); - - if (data.send_floating_position) { - data.send_floating_position = false; - [nswindow setFrameOrigin:rect.origin]; - ScheduleContextUpdates(data); - } } + } else if (data.was_zoomed) { + [data.listener addPendingWindowOperation:PENDING_OPERATION_ZOOM]; + } else { + [data.listener clearPendingWindowOperation:PENDING_OPERATION_ZOOM]; } } } @@ -2818,6 +2796,10 @@ SDL_FullscreenResult Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Windo if (!fullscreen) { Cocoa_SetWindowTitle(_this, window); data.was_zoomed = NO; + if ([data.listener windowOperationIsPending:PENDING_OPERATION_ZOOM]) { + [data.listener clearPendingWindowOperation:PENDING_OPERATION_ZOOM]; + [nswindow zoom:nil]; + } } if (SDL_ShouldAllowTopmost() && fullscreen) { diff --git a/src/video/dummy/SDL_nullvideo.c b/src/video/dummy/SDL_nullvideo.c index 98b485bed..295176c6a 100644 --- a/src/video/dummy/SDL_nullvideo.c +++ b/src/video/dummy/SDL_nullvideo.c @@ -57,13 +57,13 @@ static void DUMMY_VideoQuit(SDL_VideoDevice *_this); static bool DUMMY_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) { - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, window->floating.x, window->floating.y); + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, window->pending.x, window->pending.y); return true; } static void DUMMY_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) { - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->floating.w, window->floating.h); + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->pending.w, window->pending.h); } // DUMMY driver bootstrap functions diff --git a/src/video/emscripten/SDL_emscriptenvideo.c b/src/video/emscripten/SDL_emscriptenvideo.c index d7515bb2f..45a6e9a1d 100644 --- a/src/video/emscripten/SDL_emscriptenvideo.c +++ b/src/video/emscripten/SDL_emscriptenvideo.c @@ -327,14 +327,14 @@ static void Emscripten_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) if (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) { data->pixel_ratio = emscripten_get_device_pixel_ratio(); } - emscripten_set_canvas_element_size(data->canvas_id, SDL_lroundf(window->floating.w * data->pixel_ratio), SDL_lroundf(window->floating.h * data->pixel_ratio)); + emscripten_set_canvas_element_size(data->canvas_id, SDL_lroundf(window->pending.w * data->pixel_ratio), SDL_lroundf(window->pending.h * data->pixel_ratio)); // scale canvas down if (!data->external_size && data->pixel_ratio != 1.0f) { - emscripten_set_element_css_size(data->canvas_id, window->floating.w, window->floating.h); + emscripten_set_element_css_size(data->canvas_id, window->pending.w, window->pending.h); } - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->floating.w, window->floating.h); + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->pending.w, window->pending.h); } } diff --git a/src/video/haiku/SDL_bwindow.cc b/src/video/haiku/SDL_bwindow.cc index de9bc2484..902676a53 100644 --- a/src/video/haiku/SDL_bwindow.cc +++ b/src/video/haiku/SDL_bwindow.cc @@ -100,8 +100,8 @@ void HAIKU_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window * window) bool HAIKU_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window * window) { BMessage msg(BWIN_MOVE_WINDOW); - msg.AddInt32("window-x", window->floating.x); - msg.AddInt32("window-y", window->floating.y); + msg.AddInt32("window-x", window->pending.x); + msg.AddInt32("window-y", window->pending.y); _ToBeWin(window)->PostMessage(&msg); return true; } @@ -109,8 +109,8 @@ bool HAIKU_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window * window) void HAIKU_SetWindowSize(SDL_VideoDevice *_this, SDL_Window * window) { BMessage msg(BWIN_RESIZE_WINDOW); - msg.AddInt32("window-w", window->floating.w - 1); - msg.AddInt32("window-h", window->floating.h - 1); + msg.AddInt32("window-w", window->pending.w - 1); + msg.AddInt32("window-h", window->pending.h - 1); _ToBeWin(window)->PostMessage(&msg); } diff --git a/src/video/offscreen/SDL_offscreenwindow.c b/src/video/offscreen/SDL_offscreenwindow.c index c76028e45..9abc85cff 100644 --- a/src/video/offscreen/SDL_offscreenwindow.c +++ b/src/video/offscreen/SDL_offscreenwindow.c @@ -85,6 +85,6 @@ void OFFSCREEN_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) void OFFSCREEN_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) { - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->floating.w, window->floating.h); + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->pending.w, window->pending.h); } #endif // SDL_VIDEO_DRIVER_OFFSCREEN diff --git a/src/video/openvr/SDL_openvrvideo.c b/src/video/openvr/SDL_openvrvideo.c index eafd71ac6..51c10dfba 100644 --- a/src/video/openvr/SDL_openvrvideo.c +++ b/src/video/openvr/SDL_openvrvideo.c @@ -1138,12 +1138,12 @@ static void OPENVR_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) { SDL_VideoData *data = (SDL_VideoData *)_this->internal; - if (window->floating.w != window->w) { - window->w = window->floating.w; + if (window->pending.w != window->w) { + window->w = window->pending.w; } - if (window->floating.h != window->h) { - window->h = window->floating.h; + if (window->pending.h != window->h) { + window->h = window->pending.h; } if (data->targh != window->h || data->targw != window->w) { diff --git a/src/video/qnx/SDL_qnxvideo.c b/src/video/qnx/SDL_qnxvideo.c index 6e7d5dc80..db2314392 100644 --- a/src/video/qnx/SDL_qnxvideo.c +++ b/src/video/qnx/SDL_qnxvideo.c @@ -246,8 +246,8 @@ static void setWindowSize(SDL_VideoDevice *_this, SDL_Window *window) window_impl_t *impl = (window_impl_t *)window->internal; int size[2]; - size[0] = window->floating.w; - size[1] = window->floating.h; + size[0] = window->pending.w; + size[1] = window->pending.h; screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_SIZE, size); screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_SOURCE_SIZE, size); diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index 0fa54dcae..31aa805cc 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -264,8 +264,8 @@ static void RepositionPopup(SDL_Window *window, bool use_current_position) if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_POPUP && wind->shell_surface.xdg.popup.xdg_positioner && xdg_popup_get_version(wind->shell_surface.xdg.popup.xdg_popup) >= XDG_POPUP_REPOSITION_SINCE_VERSION) { - int x = use_current_position ? window->x : window->floating.x; - int y = use_current_position ? window->y : window->floating.y; + int x = use_current_position ? window->x : window->pending.x; + int y = use_current_position ? window->y : window->pending.y; EnsurePopupPositionIsValid(window, &x, &y); if (wind->scale_to_display) { @@ -499,28 +499,35 @@ static struct wl_callback_listener fullscreen_deadline_listener = { fullscreen_deadline_handler }; -static void maximized_deadline_handler(void *data, struct wl_callback *callback, uint32_t callback_data) +static void maximized_restored_deadline_handler(void *data, struct wl_callback *callback, uint32_t callback_data) { // Get the window from the ID as it may have been destroyed SDL_WindowID windowID = (SDL_WindowID)((uintptr_t)data); SDL_Window *window = SDL_GetWindowFromID(windowID); if (window && window->internal) { - window->internal->maximized_deadline_count--; + window->internal->maximized_restored_deadline_count--; } wl_callback_destroy(callback); } -static struct wl_callback_listener maximized_deadline_listener = { - maximized_deadline_handler +static struct wl_callback_listener maximized_restored_deadline_listener = { + maximized_restored_deadline_handler }; static void FlushPendingEvents(SDL_Window *window) { - while (window->internal->fullscreen_deadline_count || window->internal->maximized_deadline_count) { + // Serialize and restore the pending flags, as they may be overwritten while flushing. + const bool last_position_pending = window->last_position_pending; + const bool last_size_pending = window->last_size_pending; + + while (window->internal->fullscreen_deadline_count || window->internal->maximized_restored_deadline_count) { WAYLAND_wl_display_roundtrip(window->internal->waylandData->display); } + + window->last_position_pending = last_position_pending; + window->last_size_pending = last_size_pending; } /* While we can't get window position from the compositor, we do at least know @@ -819,14 +826,11 @@ static void handle_configure_xdg_toplevel(void *data, * Ignore if less than or greater than max/min size. */ if (window->flags & SDL_WINDOW_RESIZABLE) { - if ((floating && wind->pending_restored_size) || - width == 0 || height == 0) { - /* This happens when we're being restored from a non-floating state - * with a pending floating client size, or the compositor indicates - * that the size is up to the client, so use the cached window size here. + if (width == 0 || height == 0) { + /* This happens when the compositor indicates that the size is + * up to the client, so use the cached window size here. */ if (floating) { - wind->pending_restored_size = false; width = window->floating.w; height = window->floating.h; } else { @@ -1234,14 +1238,13 @@ static void decoration_frame_configure(struct libdecor_frame *frame, * * https://gitlab.freedesktop.org/libdecor/libdecor/-/issues/34 */ - if ((floating && (wind->pending_restored_size || (!wind->floating && !(window->flags & SDL_WINDOW_BORDERLESS)))) || + if ((floating && (!wind->floating && !(window->flags & SDL_WINDOW_BORDERLESS))) || !libdecor_configuration_get_content_size(configuration, frame, &width, &height)) { /* This happens when we're being restored from a non-floating state, * or the compositor indicates that the size is up to the client, so * used the cached window size here. */ if (floating) { - wind->pending_restored_size = false; width = window->floating.w; height = window->floating.h; } else { @@ -2268,12 +2271,22 @@ void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) { SDL_WindowData *wind = window->internal; + // Not currently fullscreen or maximized, and no state pending; nothing to do. + if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) && + !wind->fullscreen_deadline_count && !wind->maximized_restored_deadline_count) { + return; + } + #ifdef HAVE_LIBDECOR_H if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) { if (!wind->shell_surface.libdecor.frame) { return; // Can't do anything yet, wait for ShowWindow } libdecor_frame_unset_maximized(wind->shell_surface.libdecor.frame); + + ++wind->maximized_restored_deadline_count; + struct wl_callback *cb = wl_display_sync(_this->internal->display); + wl_callback_add_listener(cb, &maximized_restored_deadline_listener, (void *)((uintptr_t)window->id)); } else #endif // Note that xdg-shell does NOT provide a way to unset minimize! @@ -2282,6 +2295,10 @@ void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) return; // Can't do anything yet, wait for ShowWindow } xdg_toplevel_unset_maximized(wind->shell_surface.xdg.toplevel.xdg_toplevel); + + ++wind->maximized_restored_deadline_count; + struct wl_callback *cb = wl_display_sync(_this->internal->display); + wl_callback_add_listener(cb, &maximized_restored_deadline_listener, (void *)((uintptr_t)window->id)); } } @@ -2340,6 +2357,12 @@ void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) WAYLAND_wl_display_roundtrip(_this->internal->display); } + // Not fullscreen, already maximized, and no state pending; nothing to do. + if (!(window->flags & SDL_WINDOW_FULLSCREEN) && (window->flags & SDL_WINDOW_MAXIMIZED) && + !wind->fullscreen_deadline_count && !wind->maximized_restored_deadline_count) { + return; + } + #ifdef HAVE_LIBDECOR_H if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) { if (!wind->shell_surface.libdecor.frame) { @@ -2349,6 +2372,10 @@ void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) // Commit to preserve any pending size data. wl_surface_commit(wind->surface); libdecor_frame_set_maximized(wind->shell_surface.libdecor.frame); + + ++wind->maximized_restored_deadline_count; + struct wl_callback *cb = wl_display_sync(viddata->display); + wl_callback_add_listener(cb, &maximized_restored_deadline_listener, (void *)((uintptr_t)window->id)); } else #endif if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) { @@ -2359,11 +2386,11 @@ void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) // Commit to preserve any pending size data. wl_surface_commit(wind->surface); xdg_toplevel_set_maximized(wind->shell_surface.xdg.toplevel.xdg_toplevel); - } - ++wind->maximized_deadline_count; - struct wl_callback *cb = wl_display_sync(viddata->display); - wl_callback_add_listener(cb, &maximized_deadline_listener, (void *)((uintptr_t)window->id)); + ++wind->maximized_restored_deadline_count; + struct wl_callback *cb = wl_display_sync(viddata->display); + wl_callback_add_listener(cb, &maximized_restored_deadline_listener, (void *)((uintptr_t)window->id)); + } } void Wayland_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window) @@ -2651,31 +2678,12 @@ bool Wayland_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) RepositionPopup(window, false); return true; } else if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR || wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) { - const bool use_pending_position_for_fullscreen = window->use_pending_position_for_fullscreen; - const int x = window->floating.x; - const int y = window->floating.y; - /* Catch up on any pending state before attempting to change the fullscreen window * display via a set fullscreen call to make sure the window doesn't have a pending * leave fullscreen event that it might override. */ FlushPendingEvents(window); - /* XXX: Need to restore this after the roundtrip, as the requested coordinates might - * have been overwritten by the 'real' coordinates if a display enter/leave event - * occurred. - * - * The common pattern: - * - * SDL_SetWindowPosition(); - * SDL_SetWindowFullscreen(); - * - * for positioning a desktop fullscreen window won't work without this. - */ - window->use_pending_position_for_fullscreen = use_pending_position_for_fullscreen; - window->floating.x = x; - window->floating.y = y; - if (wind->is_fullscreen) { SDL_VideoDisplay *display = SDL_GetVideoDisplayForFullscreenWindow(window); if (display && wind->last_displayID != display->id) { @@ -2693,30 +2701,33 @@ void Wayland_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) { SDL_WindowData *wind = window->internal; - /* Fullscreen windows do not get explicitly resized, and not strictly - * obeying the size of maximized windows is a protocol violation, so - * it is necessary to flush any of these pending state operations. + /* Flush any pending state operations, as fullscreen windows do not get + * explicitly resized, not strictly obeying the size of a maximized window + * is a protocol violation, and pending restore events might result in a + * configure event overwriting the requested size. * * Calling this on a custom surface is informative, so the size must * always be passed through. */ FlushPendingEvents(window); + // Maximized and fullscreen windows don't get resized. if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) || wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_CUSTOM) { if (!wind->scale_to_display) { - wind->requested.logical_width = window->floating.w; - wind->requested.logical_height = window->floating.h; + wind->requested.logical_width = window->pending.w; + wind->requested.logical_height = window->pending.h; } else { - wind->requested.logical_width = PixelToPoint(window, window->floating.w); - wind->requested.logical_height = PixelToPoint(window, window->floating.h); - wind->requested.pixel_width = window->floating.w; - wind->requested.pixel_height = window->floating.h; + wind->requested.logical_width = PixelToPoint(window, window->pending.w); + wind->requested.logical_height = PixelToPoint(window, window->pending.h); + wind->requested.pixel_width = window->pending.w; + wind->requested.pixel_height = window->pending.h; } ConfigureWindowGeometry(window); } else { - wind->pending_restored_size = true; + // Can't resize the window. + window->last_size_pending = false; } // Always commit, as this may be in response to a min/max limit change. @@ -2833,7 +2844,7 @@ bool Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window) do { WAYLAND_wl_display_roundtrip(_this->internal->display); - } while (wind->fullscreen_deadline_count || wind->maximized_deadline_count); + } while (wind->fullscreen_deadline_count || wind->maximized_restored_deadline_count); return true; } diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index 4cf0ceec9..0c43a7918 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -176,7 +176,7 @@ struct SDL_WindowData SDL_DisplayID last_displayID; int fullscreen_deadline_count; - int maximized_deadline_count; + int maximized_restored_deadline_count; Uint64 last_focus_event_time_ns; bool floating; bool suspended; @@ -190,7 +190,6 @@ struct SDL_WindowData bool show_hide_sync_required; bool scale_to_display; bool reparenting_required; - bool pending_restored_size; bool double_buffer; SDL_HitTestResult hit_test_result; diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index c51ec9202..6878bbe1c 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -1432,32 +1432,6 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara break; #endif // WM_GETMINMAXINFO - case WM_WINDOWPOSCHANGING: - { - if (data->expected_resize) { - returnCode = 0; - } - - if (data->floating_rect_pending && - !IsIconic(hwnd) && - !IsZoomed(hwnd) && - (data->window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED)) && - !(data->window->flags & SDL_WINDOW_FULLSCREEN)) { - // If a new floating size is pending, apply it if moving from a fixed-size to floating state. - WINDOWPOS *windowpos = (WINDOWPOS*)lParam; - int fx, fy, fw, fh; - - WIN_AdjustWindowRect(data->window, &fx, &fy, &fw, &fh, SDL_WINDOWRECT_FLOATING); - windowpos->x = fx; - windowpos->y = fy; - windowpos->cx = fw; - windowpos->cy = fh; - windowpos->flags &= ~(SWP_NOSIZE | SWP_NOMOVE); - - data->floating_rect_pending = false; - } - } break; - case WM_WINDOWPOSCHANGED: { SDL_Window *win; diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 6dbb001a6..7affc02d8 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -204,6 +204,11 @@ static bool WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, DWORD *width = window->floating.w; *height = window->floating.h; break; + case SDL_WINDOWRECT_PENDING: + SDL_RelativeToGlobalForWindow(window, window->pending.x, window->pending.y, x, y); + *width = window->pending.w; + *height = window->pending.h; + break; default: // Should never be here SDL_assert_release(false); @@ -901,10 +906,8 @@ bool WIN_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) if (!(window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) { WIN_ConstrainPopup(window); return WIN_SetWindowPositionInternal(window, - window->internal->copybits_flag | SWP_NOZORDER | SWP_NOOWNERZORDER | - SWP_NOACTIVATE, SDL_WINDOWRECT_FLOATING); - } else { - window->internal->floating_rect_pending = true; + window->internal->copybits_flag | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSIZE | + SWP_NOACTIVATE, SDL_WINDOWRECT_PENDING); } } else { return SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_ENTER, true); @@ -916,9 +919,10 @@ bool WIN_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) void WIN_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) { if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED))) { - WIN_SetWindowPositionInternal(window, window->internal->copybits_flag | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE, SDL_WINDOWRECT_FLOATING); + WIN_SetWindowPositionInternal(window, window->internal->copybits_flag | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE, SDL_WINDOWRECT_PENDING); } else { - window->internal->floating_rect_pending = true; + // Can't resize the window + window->last_size_pending = false; } } diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h index 9a448a797..9814d10ac 100644 --- a/src/video/windows/SDL_windowswindow.h +++ b/src/video/windows/SDL_windowswindow.h @@ -38,7 +38,8 @@ typedef enum SDL_WindowRect { SDL_WINDOWRECT_CURRENT, SDL_WINDOWRECT_WINDOWED, - SDL_WINDOWRECT_FLOATING + SDL_WINDOWRECT_FLOATING, + SDL_WINDOWRECT_PENDING } SDL_WindowRect; typedef enum SDL_WindowEraseBackgroundMode @@ -76,7 +77,6 @@ struct SDL_WindowData bool expected_resize; bool in_border_change; bool in_title_click; - bool floating_rect_pending; Uint8 focus_click_pending; bool skip_update_clipcursor; Uint64 last_updated_clipcursor; diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 9b26bf092..94285e429 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -1772,16 +1772,23 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) } if (!(flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) { data->pending_operation &= ~X11_PENDING_OP_RESTORE; - if (SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0)) { - // Restore the last known floating state if leaving maximized mode - if (!(flags & SDL_WINDOW_FULLSCREEN)) { - data->pending_operation |= X11_PENDING_OP_MOVE | X11_PENDING_OP_RESIZE; - data->expected.x = data->window->floating.x - data->border_left; - data->expected.y = data->window->floating.y - data->border_top; - data->expected.w = data->window->floating.w; - data->expected.h = data->window->floating.h; - X11_XMoveWindow(display, data->xwindow, data->window->floating.x - data->border_left, data->window->floating.y - data->border_top); - X11_XResizeWindow(display, data->xwindow, data->window->floating.w, data->window->floating.h); + SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0); + + // Apply any pending state if restored. + if (!(flags & SDL_WINDOW_FULLSCREEN)) { + if (data->pending_position) { + data->pending_position = false; + data->pending_operation |= X11_PENDING_OP_MOVE; + data->expected.x = data->window->pending.x - data->border_left; + data->expected.y = data->window->pending.y - data->border_top; + X11_XMoveWindow(display, data->xwindow, data->window->pending.x - data->border_left, data->window->pending.y - data->border_top); + } + if (data->pending_size) { + data->pending_size = false; + data->pending_operation |= X11_PENDING_OP_RESIZE; + data->expected.w = data->window->pending.w; + data->expected.h = data->window->pending.h; + X11_XResizeWindow(display, data->xwindow, data->window->pending.w, data->window->pending.h); } } } @@ -1812,18 +1819,28 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) X11_GetBorderValues(data); if (data->border_top != 0 || data->border_left != 0 || data->border_right != 0 || data->border_bottom != 0) { // Adjust if the window size/position changed to accommodate the borders. - if (data->window->flags & SDL_WINDOW_MAXIMIZED) { - data->pending_operation |= X11_PENDING_OP_RESIZE; + data->pending_operation |= X11_PENDING_OP_MOVE | X11_PENDING_OP_RESIZE; + + if (data->pending_position) { + data->pending_position = false; + data->expected.x = data->window->pending.x - data->border_left; + data->expected.y = data->window->pending.y - data->border_top; + + } else { + data->expected.x = data->window->windowed.x - data->border_left; + data->expected.y = data->window->windowed.y - data->border_top; + } + + if (data->pending_size) { + data->pending_size = false; + data->expected.w = data->window->pending.w; + data->expected.h = data->window->pending.h; + } else { data->expected.w = data->window->windowed.w; data->expected.h = data->window->windowed.h; - X11_XResizeWindow(display, data->xwindow, data->window->windowed.w, data->window->windowed.h); - } else { - data->pending_operation |= X11_PENDING_OP_RESIZE | X11_PENDING_OP_MOVE; - data->expected.w = data->window->floating.w; - data->expected.h = data->window->floating.h; - X11_XMoveWindow(display, data->xwindow, data->window->floating.x - data->border_left, data->window->floating.y - data->border_top); - X11_XResizeWindow(display, data->xwindow, data->window->floating.w, data->window->floating.h); } + X11_XMoveWindow(display, data->xwindow, data->expected.x, data->expected.y - data->border_top); + X11_XResizeWindow(display, data->xwindow, data->expected.w, data->expected.h); } } if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) { diff --git a/src/video/x11/SDL_x11window.c b/src/video/x11/SDL_x11window.c index 1c6766a2f..6d64bfcd5 100644 --- a/src/video/x11/SDL_x11window.c +++ b/src/video/x11/SDL_x11window.c @@ -126,6 +126,18 @@ static bool X11_IsActionAllowed(SDL_Window *window, Atom action) } #endif // 0 +static void X11_FlushPendingEvents(SDL_VideoDevice *_this, SDL_Window *window) +{ + // Serialize and restore the pending flags, as they may be overwritten while flushing. + const bool last_position_pending = window->last_position_pending; + const bool last_size_pending = window->last_size_pending; + + X11_SyncWindow(_this, window); + + window->last_position_pending = last_position_pending; + window->last_size_pending = last_size_pending; +} + void X11_SetNetWMState(SDL_VideoDevice *_this, Window xwindow, SDL_WindowFlags flags) { SDL_VideoData *videodata = _this->internal; @@ -184,15 +196,15 @@ void X11_SetNetWMState(SDL_VideoDevice *_this, Window xwindow, SDL_WindowFlags f } } -static void X11_ConstrainPopup(SDL_Window *window) +static void X11_ConstrainPopup(SDL_Window *window, bool use_current_position) { // Clamp popup windows to the output borders if (SDL_WINDOW_IS_POPUP(window)) { SDL_Window *w; SDL_DisplayID displayID; SDL_Rect rect; - int abs_x = window->floating.x; - int abs_y = window->floating.y; + int abs_x = use_current_position ? window->floating.x : window->pending.x; + int abs_y = use_current_position ? window->floating.y : window->pending.y; int offset_x = 0, offset_y = 0; // Calculate the total offset from the parents @@ -673,7 +685,7 @@ bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properties } if (SDL_WINDOW_IS_POPUP(window)) { - X11_ConstrainPopup(window); + X11_ConstrainPopup(window, true); } SDL_RelativeToGlobalForWindow(window, window->floating.x, window->floating.y, @@ -1044,8 +1056,8 @@ void X11_UpdateWindowPosition(SDL_Window *window, bool use_current_position) { SDL_WindowData *data = window->internal; Display *display = data->videodata->display; - const int rel_x = use_current_position ? window->x : window->floating.x; - const int rel_y = use_current_position ? window->y : window->floating.y; + const int rel_x = use_current_position ? window->x : window->pending.x; + const int rel_y = use_current_position ? window->y : window->pending.y; SDL_RelativeToGlobalForWindow(window, rel_x - data->border_left, rel_y - data->border_top, @@ -1060,27 +1072,20 @@ bool X11_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) { // Sync any pending fullscreen or maximize events. if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE)) { - // Save state in case it is overwritten while synchronizing. - const bool use_client_fs_coords = window->use_pending_position_for_fullscreen; - const int x = window->floating.x; - const int y = window->floating.y; - - X11_SyncWindow(_this, window); - - // Restore state that may have been overwritten while synchronizing. - window->use_pending_position_for_fullscreen = use_client_fs_coords; - window->floating.x = x; - window->floating.y = y; + X11_FlushPendingEvents(_this, window); } - // Position will be set when window is de-maximized + // Set the position as pending if the window is maximized with a restore pending. if (window->flags & SDL_WINDOW_MAXIMIZED) { + if (window->internal->pending_operation & X11_PENDING_OP_RESTORE) { + window->internal->pending_position = true; + } return true; } if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { if (SDL_WINDOW_IS_POPUP(window)) { - X11_ConstrainPopup(window); + X11_ConstrainPopup(window, false); } X11_UpdateWindowPosition(window, false); } else { @@ -1113,10 +1118,10 @@ static void X11_SetWMNormalHints(SDL_VideoDevice *_this, SDL_Window *window, XSi hide/show, because there are supposedly subtle problems with doing so and transitioning from windowed to fullscreen in Unity. */ - X11_XResizeWindow(display, data->xwindow, window->floating.w, window->floating.h); + X11_XResizeWindow(display, data->xwindow, window->pending.w, window->pending.h); SDL_RelativeToGlobalForWindow(window, - window->floating.x - data->border_left, - window->floating.y - data->border_top, + window->pending.x - data->border_left, + window->pending.y - data->border_top, &dest_x, &dest_y); X11_XMoveWindow(display, data->xwindow, dest_x, dest_y); X11_XRaiseWindow(display, data->xwindow); @@ -1197,15 +1202,22 @@ void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) SDL_WindowData *data = window->internal; Display *display = data->videodata->display; - /* Wait for pending maximize operations to complete, or the window can end up in a weird, - * partially-maximized state. + /* Wait for pending maximize and fullscreen operations to complete, as these windows + * don't get size changes. */ if (data->pending_operation & (X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_FULLSCREEN)) { - X11_SyncWindow(_this, window); + X11_FlushPendingEvents(_this, window); } - // Don't try to resize a maximized or fullscreen window, it will be done on restore. + // Set the size as pending if the window is being restored. if (window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_FULLSCREEN)) { + // New size will be set when the window is restored. + if (data->pending_operation & X11_PENDING_OP_RESTORE) { + data->pending_size = true; + } else { + // Can't resize the window. + window->last_size_pending = false; + } return; } @@ -1219,8 +1231,8 @@ void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints); - sizehints->min_width = sizehints->max_width = window->floating.w; - sizehints->min_height = sizehints->max_height = window->floating.h; + sizehints->min_width = sizehints->max_width = window->pending.w; + sizehints->min_height = sizehints->max_height = window->pending.h; sizehints->flags |= PMinSize | PMaxSize; X11_SetWMNormalHints(_this, window, sizehints); @@ -1228,8 +1240,8 @@ void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) X11_XFree(sizehints); } } else { - data->expected.w = window->floating.w; - data->expected.h = window->floating.h; + data->expected.w = window->pending.w; + data->expected.h = window->pending.h; data->pending_operation |= X11_PENDING_OP_RESIZE; X11_XResizeWindow(display, data->xwindow, data->expected.w, data->expected.h); } @@ -1577,7 +1589,7 @@ static bool X11_SetWindowMaximized(SDL_VideoDevice *_this, SDL_Window *window, b Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT; Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ; - if (!maximized && window->flags & SDL_WINDOW_FULLSCREEN) { + if (window->flags & SDL_WINDOW_FULLSCREEN) { /* Fullscreen windows are maximized on some window managers, and this is functional behavior, so don't remove that state now, we'll take care of it when we leave fullscreen mode. @@ -1633,7 +1645,13 @@ void X11_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) SDL_SyncWindow(window); } - if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MINIMIZED))) { + if (window->flags & SDL_WINDOW_FULLSCREEN) { + // If fullscreen, just toggle the restored state. + window->internal->window_was_maximized = true; + return; + } + + if (!(window->flags & SDL_WINDOW_MINIMIZED)) { window->internal->pending_operation |= X11_PENDING_OP_MAXIMIZE; X11_SetWindowMaximized(_this, window, true); } @@ -1645,8 +1663,14 @@ void X11_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window) SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); Display *display = data->videodata->display; + if (data->pending_operation & SDL_WINDOW_FULLSCREEN) { + SDL_SyncWindow(window); + } + data->pending_operation |= X11_PENDING_OP_MINIMIZE; - data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED); + if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { + data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED); + } X11_XIconifyWindow(display, data->xwindow, displaydata->screen); X11_XFlush(display); } @@ -1657,14 +1681,19 @@ void X11_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) SDL_SyncWindow(window); } + if ((window->flags & SDL_WINDOW_FULLSCREEN) && !(window->flags & SDL_WINDOW_MINIMIZED)) { + // If fullscreen and not minimized, just toggle the restored state. + window->internal->window_was_maximized = false; + return; + } + if (window->flags & (SDL_WINDOW_MINIMIZED | SDL_WINDOW_MAXIMIZED) || (window->internal->pending_operation & X11_PENDING_OP_MINIMIZE)) { window->internal->pending_operation |= X11_PENDING_OP_RESTORE; } // If the window was minimized while maximized, restore as maximized. - const bool maximize = !!(window->flags & SDL_WINDOW_MINIMIZED) && window->internal->window_was_maximized; - window->internal->window_was_maximized = false; + const bool maximize = !!(window->flags & SDL_WINDOW_MINIMIZED) && window->internal->window_was_maximized; X11_SetWindowMaximized(_this, window, maximize); X11_ShowWindow(_this, window); X11_SetWindowActive(_this, window); diff --git a/src/video/x11/SDL_x11window.h b/src/video/x11/SDL_x11window.h index 251ae677b..8a9e8b297 100644 --- a/src/video/x11/SDL_x11window.h +++ b/src/video/x11/SDL_x11window.h @@ -103,6 +103,8 @@ struct SDL_WindowData X11_PENDING_OP_RESIZE = 0x20 } pending_operation; + bool pending_size; + bool pending_position; bool window_was_maximized; bool disable_size_position_events; bool previous_borders_nonzero; diff --git a/test/testautomation_video.c b/test/testautomation_video.c index b16edc725..5547f965a 100644 --- a/test/testautomation_video.c +++ b/test/testautomation_video.c @@ -2133,7 +2133,54 @@ static int SDLCALL video_getSetWindowState(void *arg) SDLTest_AssertCheck(windowedW == currentW, "Verify returned width; expected: %d, got: %d", windowedW, currentW); SDLTest_AssertCheck(windowedH == currentH, "Verify returned height; expected: %d, got: %d", windowedH, currentH); - /* Maximize, change size, and restore */ + /* Maximize, restore, and change size */ + result = SDL_MaximizeWindow(window); + SDLTest_AssertPass("SDL_MaximizeWindow()"); + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); + + result = SDL_RestoreWindow(window); + SDLTest_AssertPass("SDL_RestoreWindow()"); + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); + + desiredW = windowedW + 10; + desiredH = windowedH + 10; + result = SDL_SetWindowSize(window, desiredW, desiredH); + SDLTest_AssertPass("SDL_SetWindowSize()"); + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); + + if (!skipPos) { + desiredX = windowedX + 10; + desiredY = windowedY + 10; + result = SDL_SetWindowPosition(window, desiredX, desiredY); + SDLTest_AssertPass("SDL_SetWindowPosition()"); + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); + } + + result = SDL_SyncWindow(window); + SDLTest_AssertPass("SDL_SyncWindow()"); + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); + + flags = SDL_GetWindowFlags(window); + SDLTest_AssertPass("SDL_GetWindowFlags()"); + SDLTest_AssertCheck(!(flags & SDL_WINDOW_MAXIMIZED), "Verify that the `SDL_WINDOW_MAXIMIZED` flag is cleared: %s", !(flags & SDL_WINDOW_MAXIMIZED) ? "true" : "false"); + + if (!skipPos) { + currentX = desiredX + 1; + currentY = desiredY + 1; + SDL_GetWindowPosition(window, ¤tX, ¤tY); + SDLTest_AssertPass("Call to SDL_GetWindowPosition()"); + SDLTest_AssertCheck(desiredX == currentX, "Verify returned X coordinate; expected: %d, got: %d", desiredX, currentX); + SDLTest_AssertCheck(desiredY == currentY, "Verify returned Y coordinate; expected: %d, got: %d", desiredY, currentY); + } + + currentW = desiredW + 1; + currentH = desiredH + 1; + SDL_GetWindowSize(window, ¤tW, ¤tH); + SDLTest_AssertPass("Call to SDL_GetWindowSize()"); + SDLTest_AssertCheck(desiredW == currentW, "Verify returned width; expected: %d, got: %d", desiredW, currentW); + SDLTest_AssertCheck(desiredH == currentH, "Verify returned height; expected: %d, got: %d", desiredH, currentH); + + /* Maximize, change size/position (should be ignored), and restore. */ result = SDL_MaximizeWindow(window); SDLTest_AssertPass("SDL_MaximizeWindow()"); SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); @@ -2165,20 +2212,20 @@ static int SDLCALL video_getSetWindowState(void *arg) SDLTest_AssertCheck(!(flags & SDL_WINDOW_MAXIMIZED), "Verify that the `SDL_WINDOW_MAXIMIZED` flag is cleared: %s", !(flags & SDL_WINDOW_MAXIMIZED) ? "true" : "false"); if (!skipPos) { - currentX = desiredX + 1; - currentY = desiredY + 1; - SDL_GetWindowPosition(window, ¤tX, ¤tY); + int previousX = desiredX + 1; + int previousY = desiredY + 1; + SDL_GetWindowPosition(window, &previousX, &previousY); SDLTest_AssertPass("Call to SDL_GetWindowPosition()"); - SDLTest_AssertCheck(desiredX == currentX, "Verify returned X coordinate; expected: %d, got: %d", desiredX, currentX); - SDLTest_AssertCheck(desiredY == currentY, "Verify returned Y coordinate; expected: %d, got: %d", desiredY, currentY); + SDLTest_AssertCheck(desiredX == currentX, "Verify returned X coordinate; expected: %d, got: %d", previousX, currentX); + SDLTest_AssertCheck(desiredY == currentY, "Verify returned Y coordinate; expected: %d, got: %d", previousY, currentY); } - currentW = desiredW + 1; - currentH = desiredH + 1; - SDL_GetWindowSize(window, ¤tW, ¤tH); + int previousW = desiredW + 1; + int previousH = desiredH + 1; + SDL_GetWindowSize(window, &previousW, &previousH); SDLTest_AssertPass("Call to SDL_GetWindowSize()"); - SDLTest_AssertCheck(desiredW == currentW, "Verify returned width; expected: %d, got: %d", desiredW, currentW); - SDLTest_AssertCheck(desiredH == currentH, "Verify returned height; expected: %d, got: %d", desiredH, currentH); + SDLTest_AssertCheck(desiredW == currentW, "Verify returned width; expected: %d, got: %d", previousW, currentW); + SDLTest_AssertCheck(desiredH == currentH, "Verify returned height; expected: %d, got: %d", previousH, currentH); /* Change size and position, maximize and restore */ desiredW = windowedW - 5;