Simple Directmedia Layer

video: Drop size and position requests for windows in a fixed size/position state

It is not uncommon for clients to redundantly set the window size and position, either as a holdover from an SDL 1 port, when this was required, due to any window state change triggering a universal update function that sets all window state, even if unnecessary (e.g. always calling SDL_SetWindowSize(), even if the window is fullscreen), or due to the use of compatability layers. Historically, these clients expect that their behavior won't override the base window state, which is an assumption that the windowing changes in SDL 3 broke by caching size and position changes that can't be applied immediately.

This change drops size and position requests when the window is in the maximized and fullscreen states (fullscreen-desktop windows will be repositioned, but the non-fullscreen floating position will not be overwritten), which is behavior more in line with existing client assumptions, and should ease the porting process, as well as prevent annoying bugs when older software is run via sdl2-compat.

In the process of making these changes, pending window state has been moved to separate variables in the SDL_Window struct, as this fixes bugs regarding fullscreen display selection and centering windows immediately after resize on asynchronous platforms, which had issues due to pending state possibly being overwritten.

+353 -277
+14 -10
include/SDL3/SDL_video.h
··· 1529 1529 /** 1530 1530 * Request that the window's position be set. 1531 1531 * 1532 - * If, at the time of this request, the window is in a fixed-size state such 1533 - * as maximized, this request may be deferred until the window returns to a 1534 - * resizable state. 1532 + * If the window is in an exclusive fullscreen or maximized state, this request 1533 + * has no effect. 1535 1534 * 1536 1535 * This can be used to reposition fullscreen-desktop windows onto a different 1537 - * display, however, exclusive fullscreen windows are locked to a specific 1538 - * display and can only be repositioned programmatically via 1536 + * display, however, as exclusive fullscreen windows are locked to a specific 1537 + * display, they can only be repositioned programmatically via 1539 1538 * SDL_SetWindowFullscreenMode(). 1540 1539 * 1541 1540 * On some windowing systems this request is asynchronous and the new ··· 1596 1595 /** 1597 1596 * Request that the size of a window's client area be set. 1598 1597 * 1599 - * If, at the time of this request, the window in a fixed-size state, such as 1600 - * maximized or fullscreen, the request will be deferred until the window 1601 - * exits this state and becomes resizable again. 1598 + * If the window is in a fullscreen or maximized state, this request has no 1599 + * effect. 1602 1600 * 1603 - * To change the fullscreen mode of a window, use 1604 - * SDL_SetWindowFullscreenMode() 1601 + * To change the exclusive fullscreen mode of a window, use 1602 + * SDL_SetWindowFullscreenMode(). 1605 1603 * 1606 1604 * On some windowing systems, this request is asynchronous and the new window 1607 1605 * size may not have have been applied immediately upon the return of this ··· 2021 2019 /** 2022 2020 * Request that the window be minimized to an iconic representation. 2023 2021 * 2022 + * If the window is in a fullscreen state, this request has no direct effect. 2023 + * It may alter the state the window is returned to when leaving fullscreen. 2024 + * 2024 2025 * On some windowing systems this request is asynchronous and the new window 2025 2026 * state may not have been applied immediately upon the return of this 2026 2027 * function. If an immediate change is required, call SDL_SyncWindow() to ··· 2047 2048 /** 2048 2049 * Request that the size and position of a minimized or maximized window be 2049 2050 * restored. 2051 + * 2052 + * If the window is in a fullscreen state, this request has no direct effect. 2053 + * It may alter the state the window is returned to when leaving fullscreen. 2050 2054 * 2051 2055 * On some windowing systems this request is asynchronous and the new window 2052 2056 * state may not have have been applied immediately upon the return of this
+2 -1
src/events/SDL_windowevents.c
··· 69 69 case SDL_EVENT_WINDOW_MOVED: 70 70 window->undefined_x = false; 71 71 window->undefined_y = false; 72 - window->use_pending_position_for_fullscreen = false; 72 + window->last_position_pending = false; 73 73 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 74 74 window->windowed.x = data1; 75 75 window->windowed.y = data2; ··· 86 86 window->y = data2; 87 87 break; 88 88 case SDL_EVENT_WINDOW_RESIZED: 89 + window->last_size_pending = false; 89 90 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 90 91 window->windowed.w = data1; 91 92 window->windowed.h = data2;
+5 -1
src/video/SDL_sysvideo.h
··· 78 78 */ 79 79 SDL_Rect floating; 80 80 81 + // The last client requested size and position for the window. 82 + SDL_Rect pending; 83 + 81 84 /* Toggle for drivers to indicate that the current window state is tiled, 82 85 * and sizes set non-programmatically shouldn't be cached. 83 86 */ ··· 98 101 99 102 bool is_hiding; 100 103 bool restore_on_show; // Child was hidden recursively by the parent, restore when shown. 101 - bool use_pending_position_for_fullscreen; 104 + bool last_position_pending; // This should NOT be cleared by the backend, as it is used for fullscreen positioning. 105 + bool last_size_pending; // This should be cleared by the backend if the new size cannot be applied. 102 106 bool is_destroying; 103 107 bool is_dropping; // drag/drop in progress, expecting SDL_SendDropComplete(). 104 108
+19 -18
src/video/SDL_video.c
··· 1664 1664 * the current position won't be updated at the time of the fullscreen call. 1665 1665 */ 1666 1666 if (!displayID) { 1667 - if (window->use_pending_position_for_fullscreen) { 1668 - // The last coordinates were client requested; use the pending floating coordinates. 1669 - displayID = GetDisplayForRect(window->floating.x, window->floating.y, window->floating.w, window->floating.h); 1670 - } 1671 - else { 1672 - // The last coordinates were from the window manager; use the current position. 1673 - displayID = GetDisplayForRect(window->x, window->y, 1, 1); 1674 - } 1667 + // Use the pending position and dimensions, if available, otherwise, use the current. 1668 + const int x = window->last_position_pending ? window->pending.x : window->x; 1669 + const int y = window->last_position_pending ? window->pending.y : window->y; 1670 + const int w = window->last_size_pending ? window->pending.w : window->w; 1671 + const int h = window->last_size_pending ? window->pending.h : window->h; 1672 + 1673 + displayID = GetDisplayForRect(x, y, w, h); 1675 1674 } 1676 1675 if (!displayID) { 1677 1676 // Use the primary display for a window if we can't find it anywhere else ··· 2763 2762 2764 2763 CHECK_WINDOW_MAGIC(window, false); 2765 2764 2765 + const int w = window->last_size_pending ? window->pending.w : window->windowed.w; 2766 + const int h = window->last_size_pending ? window->pending.h : window->windowed.h; 2767 + 2766 2768 original_displayID = SDL_GetDisplayForWindow(window); 2767 2769 2768 2770 if (SDL_WINDOWPOS_ISUNDEFINED(x)) { ··· 2785 2787 } 2786 2788 2787 2789 SDL_zero(bounds); 2788 - if (!SDL_GetDisplayUsableBounds(displayID, &bounds) || 2789 - window->windowed.w > bounds.w || 2790 - window->windowed.h > bounds.h) { 2790 + if (!SDL_GetDisplayUsableBounds(displayID, &bounds) || w > bounds.w || h > bounds.h) { 2791 2791 if (!SDL_GetDisplayBounds(displayID, &bounds)) { 2792 2792 return false; 2793 2793 } 2794 2794 } 2795 2795 if (SDL_WINDOWPOS_ISCENTERED(x)) { 2796 - x = bounds.x + (bounds.w - window->windowed.w) / 2; 2796 + x = bounds.x + (bounds.w - w) / 2; 2797 2797 } 2798 2798 if (SDL_WINDOWPOS_ISCENTERED(y)) { 2799 - y = bounds.y + (bounds.h - window->windowed.h) / 2; 2799 + y = bounds.y + (bounds.h - h) / 2; 2800 2800 } 2801 2801 } 2802 2802 2803 - window->floating.x = x; 2804 - window->floating.y = y; 2803 + window->pending.x = x; 2804 + window->pending.y = y; 2805 2805 window->undefined_x = false; 2806 2806 window->undefined_y = false; 2807 - window->use_pending_position_for_fullscreen = true; 2807 + window->last_position_pending = true; 2808 2808 2809 2809 if (_this->SetWindowPosition) { 2810 2810 const bool result = _this->SetWindowPosition(_this, window); ··· 2952 2952 h = window->max_h; 2953 2953 } 2954 2954 2955 - window->floating.w = w; 2956 - window->floating.h = h; 2955 + window->last_size_pending = true; 2956 + window->pending.w = w; 2957 + window->pending.h = h; 2957 2958 2958 2959 if (_this->SetWindowSize) { 2959 2960 _this->SetWindowSize(_this, window);
+5 -4
src/video/cocoa/SDL_cocoawindow.h
··· 38 38 PENDING_OPERATION_NONE = 0x00, 39 39 PENDING_OPERATION_ENTER_FULLSCREEN = 0x01, 40 40 PENDING_OPERATION_LEAVE_FULLSCREEN = 0x02, 41 - PENDING_OPERATION_MINIMIZE = 0x04 41 + PENDING_OPERATION_MINIMIZE = 0x04, 42 + PENDING_OPERATION_ZOOM = 0x08 42 43 } PendingWindowOperation; 43 44 44 45 @interface SDL3Cocoa_WindowListener : NSResponder <NSWindowDelegate> ··· 144 145 @property(nonatomic) SDL3Cocoa_WindowListener *listener; 145 146 @property(nonatomic) NSModalSession modal_session; 146 147 @property(nonatomic) SDL_CocoaVideoData *videodata; 147 - @property(nonatomic) bool send_floating_size; 148 - @property(nonatomic) bool send_floating_position; 148 + @property(nonatomic) bool pending_size; 149 + @property(nonatomic) bool pending_position; 149 150 @property(nonatomic) bool border_toggled; 150 - @property(nonatomic) BOOL checking_zoom; 151 + 151 152 #ifdef SDL_VIDEO_OPENGL_EGL 152 153 @property(nonatomic) EGLSurface egl_surface; 153 154 #endif
+65 -83
src/video/cocoa/SDL_cocoawindow.m
··· 666 666 } 667 667 } 668 668 669 - static bool Cocoa_IsZoomed(SDL_Window *window) 670 - { 671 - SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal; 672 - 673 - data.checking_zoom = YES; 674 - const bool ret = [data.nswindow isZoomed]; 675 - data.checking_zoom = NO; 676 - 677 - return ret; 678 - } 679 - 680 669 static NSCursor *Cocoa_GetDesiredCursor(void) 681 670 { 682 671 SDL_Mouse *mouse = SDL_GetMouse(); ··· 1043 1032 { 1044 1033 SDL_Window *window = _data.window; 1045 1034 1046 - /* XXX: Calling [isZoomed] calls this function, and calling [isZoomed] 1047 - * from within this function will recurse until the stack overflows, 1048 - * so a recursion guard is required. 1049 - */ 1050 - if (!_data.checking_zoom) { 1051 - _data.checking_zoom = YES; 1052 - if ([_data.nswindow isZoomed] && !_data.was_zoomed && _data.send_floating_size) { 1053 - NSRect rect; 1054 - 1055 - _data.send_floating_size = NO; 1056 - rect.origin.x = window->floating.x; 1057 - rect.origin.y = window->floating.y; 1058 - rect.size.width = window->floating.w; 1059 - rect.size.height = window->floating.h; 1060 - ConvertNSRect(&rect); 1061 - 1062 - frameSize = rect.size; 1063 - } 1064 - _data.checking_zoom = NO; 1065 - } 1066 - 1067 1035 if (window->min_aspect > 0.0f || window->max_aspect > 0.0f) { 1068 1036 NSWindow *nswindow = _data.nswindow; 1069 1037 NSRect newContentRect = [nswindow contentRectForFrameRect:NSMakeRect(0, 0, frameSize.width, frameSize.height)]; ··· 1119 1087 /* isZoomed always returns true if the window is not resizable 1120 1088 * and fullscreen windows are considered zoomed. 1121 1089 */ 1122 - if ((window->flags & SDL_WINDOW_RESIZABLE) && Cocoa_IsZoomed(window) && 1090 + if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed] && 1123 1091 !(window->flags & SDL_WINDOW_FULLSCREEN) && ![self isInFullscreenSpace]) { 1124 1092 zoomed = YES; 1125 1093 } else { ··· 1164 1132 SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_RESTORED, 0, 0); 1165 1133 1166 1134 // isZoomed always returns true if the window is not resizable. 1167 - if ((_data.window->flags & SDL_WINDOW_RESIZABLE) && Cocoa_IsZoomed(_data.window)) { 1135 + if ((_data.window->flags & SDL_WINDOW_RESIZABLE) && [_data.nswindow isZoomed]) { 1168 1136 SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0); 1169 1137 } 1170 1138 ··· 1324 1292 } 1325 1293 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0); 1326 1294 1295 + _data.pending_position = NO; 1296 + _data.pending_size = NO; 1297 + 1327 1298 /* Force the size change event in case it was delivered earlier 1328 1299 while the window was still animating into place. 1329 1300 */ ··· 1361 1332 if (window->is_destroying) { 1362 1333 return; 1363 1334 } 1335 + 1336 + _data.pending_position = NO; 1337 + _data.pending_size = NO; 1338 + window->last_position_pending = false; 1339 + window->last_size_pending = false; 1364 1340 1365 1341 SetWindowStyle(window, flags); 1366 1342 ··· 1425 1401 } 1426 1402 [NSMenu setMenuBarVisible:YES]; 1427 1403 1428 - // Restore windowed size and position in case it changed while fullscreen 1429 - NSRect rect; 1430 - rect.origin.x = _data.was_zoomed ? window->windowed.x : window->floating.x; 1431 - rect.origin.y = _data.was_zoomed ? window->windowed.y : window->floating.y; 1432 - rect.size.width = _data.was_zoomed ? window->windowed.w : window->floating.w; 1433 - rect.size.height = _data.was_zoomed ? window->windowed.h : window->floating.h; 1434 - ConvertNSRect(&rect); 1404 + // Toggle zoom, if changed while fullscreen. 1405 + if ([self windowOperationIsPending:PENDING_OPERATION_ZOOM]) { 1406 + [self clearPendingWindowOperation:PENDING_OPERATION_ZOOM]; 1407 + [nswindow zoom:nil]; 1408 + } 1435 1409 1436 - _data.send_floating_position = NO; 1437 - _data.send_floating_size = NO; 1438 - [nswindow setContentSize:rect.size]; 1439 - [nswindow setFrameOrigin:rect.origin]; 1410 + if (![nswindow isZoomed]) { 1411 + // Apply a pending window size, if not zoomed. 1412 + NSRect rect; 1413 + rect.origin.x = _data.pending_position ? window->pending.x : window->floating.x; 1414 + rect.origin.y = _data.pending_position ? window->pending.y : window->floating.y; 1415 + rect.size.width = _data.pending_size ? window->pending.w : window->floating.w; 1416 + rect.size.height = _data.pending_size ? window->pending.h : window->floating.h; 1417 + ConvertNSRect(&rect); 1418 + 1419 + if (_data.pending_size) { 1420 + [nswindow setContentSize:rect.size]; 1421 + } 1422 + if (_data.pending_position) { 1423 + [nswindow setFrameOrigin:rect.origin]; 1424 + } 1425 + } 1426 + 1427 + _data.pending_size = NO; 1428 + _data.pending_position = NO; 1429 + _data.was_zoomed = NO; 1440 1430 1441 1431 /* Force the size change event in case it was delivered earlier 1442 1432 * while the window was still animating into place. ··· 1445 1435 window->h = 0; 1446 1436 [self windowDidMove:aNotification]; 1447 1437 [self windowDidResize:aNotification]; 1448 - 1449 - _data.was_zoomed = false; 1450 1438 1451 1439 // FIXME: Why does the window get hidden? 1452 1440 if (!(window->flags & SDL_WINDOW_HIDDEN)) { ··· 2083 2071 } 2084 2072 2085 2073 // isZoomed always returns true if the window is not resizable 2086 - if ((window->flags & SDL_WINDOW_RESIZABLE) && Cocoa_IsZoomed(window)) { 2074 + if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) { 2087 2075 window->flags |= SDL_WINDOW_MAXIMIZED; 2088 2076 } else { 2089 2077 window->flags &= ~SDL_WINDOW_MAXIMIZED; ··· 2352 2340 BOOL fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN) ? YES : NO; 2353 2341 int x, y; 2354 2342 2355 - if ([windata.listener windowOperationIsPending:(PENDING_OPERATION_ENTER_FULLSCREEN | PENDING_OPERATION_LEAVE_FULLSCREEN)] || 2356 - [windata.listener isInFullscreenSpaceTransition]) { 2357 - Cocoa_SyncWindow(_this, window); 2343 + if ([windata.listener isInFullscreenSpaceTransition]) { 2344 + windata.pending_position = YES; 2345 + return true; 2358 2346 } 2359 2347 2360 2348 if (!(window->flags & SDL_WINDOW_MAXIMIZED)) { ··· 2366 2354 rect.origin.x = r.x; 2367 2355 rect.origin.y = r.y; 2368 2356 } else { 2369 - SDL_RelativeToGlobalForWindow(window, window->floating.x, window->floating.y, &x, &y); 2357 + SDL_RelativeToGlobalForWindow(window, window->pending.x, window->pending.y, &x, &y); 2370 2358 rect.origin.x = x; 2371 2359 rect.origin.y = y; 2372 2360 } ··· 2389 2377 [nswindow setFrameOrigin:rect.origin]; 2390 2378 2391 2379 ScheduleContextUpdates(windata); 2392 - } else { 2393 - windata.send_floating_position = true; 2394 2380 } 2395 2381 } 2396 2382 return true; ··· 2402 2388 SDL_CocoaWindowData *windata = (__bridge SDL_CocoaWindowData *)window->internal; 2403 2389 NSWindow *nswindow = windata.nswindow; 2404 2390 2405 - if ([windata.listener windowOperationIsPending:(PENDING_OPERATION_ENTER_FULLSCREEN | PENDING_OPERATION_LEAVE_FULLSCREEN)] || 2406 - [windata.listener isInFullscreenSpaceTransition]) { 2407 - Cocoa_SyncWindow(_this, window); 2391 + if ([windata.listener isInFullscreenSpaceTransition]) { 2392 + windata.pending_size = YES; 2393 + return; 2408 2394 } 2409 2395 2410 2396 // isZoomed always returns true if the window is not resizable 2411 - if (!(window->flags & SDL_WINDOW_RESIZABLE) || !Cocoa_IsZoomed(window)) { 2397 + if (!(window->flags & SDL_WINDOW_RESIZABLE) || ![nswindow isZoomed]) { 2412 2398 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 2413 2399 int x, y; 2414 2400 NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]]; ··· 2420 2406 SDL_RelativeToGlobalForWindow(window, window->floating.x, window->floating.y, &x, &y); 2421 2407 rect.origin.x = x; 2422 2408 rect.origin.y = y; 2423 - rect.size.width = window->floating.w; 2424 - rect.size.height = window->floating.h; 2409 + rect.size.width = window->pending.w; 2410 + rect.size.height = window->pending.h; 2425 2411 ConvertNSRect(&rect); 2426 2412 2427 2413 [nswindow setFrame:[nswindow frameRectForContentRect:rect] display:YES]; 2428 2414 ScheduleContextUpdates(windata); 2429 - } else if (windata.was_zoomed) { 2430 - windata.send_floating_size = true; 2415 + } else { 2416 + // Can't set the window size. 2417 + window->last_size_pending = false; 2431 2418 } 2432 - } else { 2433 - windata.send_floating_size = true; 2419 + } else { 2420 + // Can't set the window size. 2421 + window->last_size_pending = false; 2434 2422 } 2435 2423 } 2436 2424 } ··· 2614 2602 ![windata.listener isInFullscreenSpace]) { 2615 2603 [nswindow zoom:nil]; 2616 2604 ScheduleContextUpdates(windata); 2605 + } else if (!windata.was_zoomed) { 2606 + [windata.listener addPendingWindowOperation:PENDING_OPERATION_ZOOM]; 2607 + } else { 2608 + [windata.listener clearPendingWindowOperation:PENDING_OPERATION_ZOOM]; 2617 2609 } 2618 2610 } 2619 2611 } ··· 2654 2646 ![data.listener isInFullscreenSpace]) { 2655 2647 if ([nswindow isMiniaturized]) { 2656 2648 [nswindow deminiaturize:nil]; 2657 - } else if ((window->flags & SDL_WINDOW_RESIZABLE) && Cocoa_IsZoomed(window)) { 2658 - NSRect rect; 2659 - 2660 - // Update the floating coordinates 2661 - rect.origin.x = window->floating.x; 2662 - rect.origin.y = window->floating.y; 2663 - 2664 - // The floating size will be set in windowWillResize 2649 + } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [data.nswindow isZoomed]) { 2665 2650 [nswindow zoom:nil]; 2666 - 2667 - rect.size.width = window->floating.w; 2668 - rect.size.height = window->floating.h; 2669 - 2670 - ConvertNSRect(&rect); 2671 - 2672 - if (data.send_floating_position) { 2673 - data.send_floating_position = false; 2674 - [nswindow setFrameOrigin:rect.origin]; 2675 - ScheduleContextUpdates(data); 2676 - } 2677 2651 } 2652 + } else if (data.was_zoomed) { 2653 + [data.listener addPendingWindowOperation:PENDING_OPERATION_ZOOM]; 2654 + } else { 2655 + [data.listener clearPendingWindowOperation:PENDING_OPERATION_ZOOM]; 2678 2656 } 2679 2657 } 2680 2658 } ··· 2818 2796 if (!fullscreen) { 2819 2797 Cocoa_SetWindowTitle(_this, window); 2820 2798 data.was_zoomed = NO; 2799 + if ([data.listener windowOperationIsPending:PENDING_OPERATION_ZOOM]) { 2800 + [data.listener clearPendingWindowOperation:PENDING_OPERATION_ZOOM]; 2801 + [nswindow zoom:nil]; 2802 + } 2821 2803 } 2822 2804 2823 2805 if (SDL_ShouldAllowTopmost() && fullscreen) {
+2 -2
src/video/dummy/SDL_nullvideo.c
··· 57 57 58 58 static bool DUMMY_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) 59 59 { 60 - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, window->floating.x, window->floating.y); 60 + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, window->pending.x, window->pending.y); 61 61 return true; 62 62 } 63 63 64 64 static void DUMMY_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) 65 65 { 66 - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->floating.w, window->floating.h); 66 + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->pending.w, window->pending.h); 67 67 } 68 68 69 69 // DUMMY driver bootstrap functions
+3 -3
src/video/emscripten/SDL_emscriptenvideo.c
··· 327 327 if (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) { 328 328 data->pixel_ratio = emscripten_get_device_pixel_ratio(); 329 329 } 330 - emscripten_set_canvas_element_size(data->canvas_id, SDL_lroundf(window->floating.w * data->pixel_ratio), SDL_lroundf(window->floating.h * data->pixel_ratio)); 330 + emscripten_set_canvas_element_size(data->canvas_id, SDL_lroundf(window->pending.w * data->pixel_ratio), SDL_lroundf(window->pending.h * data->pixel_ratio)); 331 331 332 332 // scale canvas down 333 333 if (!data->external_size && data->pixel_ratio != 1.0f) { 334 - emscripten_set_element_css_size(data->canvas_id, window->floating.w, window->floating.h); 334 + emscripten_set_element_css_size(data->canvas_id, window->pending.w, window->pending.h); 335 335 } 336 336 337 - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->floating.w, window->floating.h); 337 + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->pending.w, window->pending.h); 338 338 } 339 339 } 340 340
+4 -4
src/video/haiku/SDL_bwindow.cc
··· 100 100 bool HAIKU_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window * window) 101 101 { 102 102 BMessage msg(BWIN_MOVE_WINDOW); 103 - msg.AddInt32("window-x", window->floating.x); 104 - msg.AddInt32("window-y", window->floating.y); 103 + msg.AddInt32("window-x", window->pending.x); 104 + msg.AddInt32("window-y", window->pending.y); 105 105 _ToBeWin(window)->PostMessage(&msg); 106 106 return true; 107 107 } ··· 109 109 void HAIKU_SetWindowSize(SDL_VideoDevice *_this, SDL_Window * window) 110 110 { 111 111 BMessage msg(BWIN_RESIZE_WINDOW); 112 - msg.AddInt32("window-w", window->floating.w - 1); 113 - msg.AddInt32("window-h", window->floating.h - 1); 112 + msg.AddInt32("window-w", window->pending.w - 1); 113 + msg.AddInt32("window-h", window->pending.h - 1); 114 114 _ToBeWin(window)->PostMessage(&msg); 115 115 } 116 116
+1 -1
src/video/offscreen/SDL_offscreenwindow.c
··· 85 85 86 86 void OFFSCREEN_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) 87 87 { 88 - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->floating.w, window->floating.h); 88 + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->pending.w, window->pending.h); 89 89 } 90 90 #endif // SDL_VIDEO_DRIVER_OFFSCREEN
+4 -4
src/video/openvr/SDL_openvrvideo.c
··· 1138 1138 { 1139 1139 SDL_VideoData *data = (SDL_VideoData *)_this->internal; 1140 1140 1141 - if (window->floating.w != window->w) { 1142 - window->w = window->floating.w; 1141 + if (window->pending.w != window->w) { 1142 + window->w = window->pending.w; 1143 1143 } 1144 1144 1145 - if (window->floating.h != window->h) { 1146 - window->h = window->floating.h; 1145 + if (window->pending.h != window->h) { 1146 + window->h = window->pending.h; 1147 1147 } 1148 1148 1149 1149 if (data->targh != window->h || data->targw != window->w) {
+2 -2
src/video/qnx/SDL_qnxvideo.c
··· 246 246 window_impl_t *impl = (window_impl_t *)window->internal; 247 247 int size[2]; 248 248 249 - size[0] = window->floating.w; 250 - size[1] = window->floating.h; 249 + size[0] = window->pending.w; 250 + size[1] = window->pending.h; 251 251 252 252 screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_SIZE, size); 253 253 screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_SOURCE_SIZE, size);
+60 -49
src/video/wayland/SDL_waylandwindow.c
··· 264 264 if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_POPUP && 265 265 wind->shell_surface.xdg.popup.xdg_positioner && 266 266 xdg_popup_get_version(wind->shell_surface.xdg.popup.xdg_popup) >= XDG_POPUP_REPOSITION_SINCE_VERSION) { 267 - int x = use_current_position ? window->x : window->floating.x; 268 - int y = use_current_position ? window->y : window->floating.y; 267 + int x = use_current_position ? window->x : window->pending.x; 268 + int y = use_current_position ? window->y : window->pending.y; 269 269 270 270 EnsurePopupPositionIsValid(window, &x, &y); 271 271 if (wind->scale_to_display) { ··· 499 499 fullscreen_deadline_handler 500 500 }; 501 501 502 - static void maximized_deadline_handler(void *data, struct wl_callback *callback, uint32_t callback_data) 502 + static void maximized_restored_deadline_handler(void *data, struct wl_callback *callback, uint32_t callback_data) 503 503 { 504 504 // Get the window from the ID as it may have been destroyed 505 505 SDL_WindowID windowID = (SDL_WindowID)((uintptr_t)data); 506 506 SDL_Window *window = SDL_GetWindowFromID(windowID); 507 507 508 508 if (window && window->internal) { 509 - window->internal->maximized_deadline_count--; 509 + window->internal->maximized_restored_deadline_count--; 510 510 } 511 511 512 512 wl_callback_destroy(callback); 513 513 } 514 514 515 - static struct wl_callback_listener maximized_deadline_listener = { 516 - maximized_deadline_handler 515 + static struct wl_callback_listener maximized_restored_deadline_listener = { 516 + maximized_restored_deadline_handler 517 517 }; 518 518 519 519 static void FlushPendingEvents(SDL_Window *window) 520 520 { 521 - while (window->internal->fullscreen_deadline_count || window->internal->maximized_deadline_count) { 521 + // Serialize and restore the pending flags, as they may be overwritten while flushing. 522 + const bool last_position_pending = window->last_position_pending; 523 + const bool last_size_pending = window->last_size_pending; 524 + 525 + while (window->internal->fullscreen_deadline_count || window->internal->maximized_restored_deadline_count) { 522 526 WAYLAND_wl_display_roundtrip(window->internal->waylandData->display); 523 527 } 528 + 529 + window->last_position_pending = last_position_pending; 530 + window->last_size_pending = last_size_pending; 524 531 } 525 532 526 533 /* While we can't get window position from the compositor, we do at least know ··· 819 826 * Ignore if less than or greater than max/min size. 820 827 */ 821 828 if (window->flags & SDL_WINDOW_RESIZABLE) { 822 - if ((floating && wind->pending_restored_size) || 823 - width == 0 || height == 0) { 824 - /* This happens when we're being restored from a non-floating state 825 - * with a pending floating client size, or the compositor indicates 826 - * that the size is up to the client, so use the cached window size here. 829 + if (width == 0 || height == 0) { 830 + /* This happens when the compositor indicates that the size is 831 + * up to the client, so use the cached window size here. 827 832 */ 828 833 if (floating) { 829 - wind->pending_restored_size = false; 830 834 width = window->floating.w; 831 835 height = window->floating.h; 832 836 } else { ··· 1234 1238 * 1235 1239 * https://gitlab.freedesktop.org/libdecor/libdecor/-/issues/34 1236 1240 */ 1237 - if ((floating && (wind->pending_restored_size || (!wind->floating && !(window->flags & SDL_WINDOW_BORDERLESS)))) || 1241 + if ((floating && (!wind->floating && !(window->flags & SDL_WINDOW_BORDERLESS))) || 1238 1242 !libdecor_configuration_get_content_size(configuration, frame, &width, &height)) { 1239 1243 /* This happens when we're being restored from a non-floating state, 1240 1244 * or the compositor indicates that the size is up to the client, so 1241 1245 * used the cached window size here. 1242 1246 */ 1243 1247 if (floating) { 1244 - wind->pending_restored_size = false; 1245 1248 width = window->floating.w; 1246 1249 height = window->floating.h; 1247 1250 } else { ··· 2268 2271 { 2269 2272 SDL_WindowData *wind = window->internal; 2270 2273 2274 + // Not currently fullscreen or maximized, and no state pending; nothing to do. 2275 + if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) && 2276 + !wind->fullscreen_deadline_count && !wind->maximized_restored_deadline_count) { 2277 + return; 2278 + } 2279 + 2271 2280 #ifdef HAVE_LIBDECOR_H 2272 2281 if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) { 2273 2282 if (!wind->shell_surface.libdecor.frame) { 2274 2283 return; // Can't do anything yet, wait for ShowWindow 2275 2284 } 2276 2285 libdecor_frame_unset_maximized(wind->shell_surface.libdecor.frame); 2286 + 2287 + ++wind->maximized_restored_deadline_count; 2288 + struct wl_callback *cb = wl_display_sync(_this->internal->display); 2289 + wl_callback_add_listener(cb, &maximized_restored_deadline_listener, (void *)((uintptr_t)window->id)); 2277 2290 } else 2278 2291 #endif 2279 2292 // Note that xdg-shell does NOT provide a way to unset minimize! ··· 2282 2295 return; // Can't do anything yet, wait for ShowWindow 2283 2296 } 2284 2297 xdg_toplevel_unset_maximized(wind->shell_surface.xdg.toplevel.xdg_toplevel); 2298 + 2299 + ++wind->maximized_restored_deadline_count; 2300 + struct wl_callback *cb = wl_display_sync(_this->internal->display); 2301 + wl_callback_add_listener(cb, &maximized_restored_deadline_listener, (void *)((uintptr_t)window->id)); 2285 2302 } 2286 2303 } 2287 2304 ··· 2340 2357 WAYLAND_wl_display_roundtrip(_this->internal->display); 2341 2358 } 2342 2359 2360 + // Not fullscreen, already maximized, and no state pending; nothing to do. 2361 + if (!(window->flags & SDL_WINDOW_FULLSCREEN) && (window->flags & SDL_WINDOW_MAXIMIZED) && 2362 + !wind->fullscreen_deadline_count && !wind->maximized_restored_deadline_count) { 2363 + return; 2364 + } 2365 + 2343 2366 #ifdef HAVE_LIBDECOR_H 2344 2367 if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) { 2345 2368 if (!wind->shell_surface.libdecor.frame) { ··· 2349 2372 // Commit to preserve any pending size data. 2350 2373 wl_surface_commit(wind->surface); 2351 2374 libdecor_frame_set_maximized(wind->shell_surface.libdecor.frame); 2375 + 2376 + ++wind->maximized_restored_deadline_count; 2377 + struct wl_callback *cb = wl_display_sync(viddata->display); 2378 + wl_callback_add_listener(cb, &maximized_restored_deadline_listener, (void *)((uintptr_t)window->id)); 2352 2379 } else 2353 2380 #endif 2354 2381 if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) { ··· 2359 2386 // Commit to preserve any pending size data. 2360 2387 wl_surface_commit(wind->surface); 2361 2388 xdg_toplevel_set_maximized(wind->shell_surface.xdg.toplevel.xdg_toplevel); 2362 - } 2363 2389 2364 - ++wind->maximized_deadline_count; 2365 - struct wl_callback *cb = wl_display_sync(viddata->display); 2366 - wl_callback_add_listener(cb, &maximized_deadline_listener, (void *)((uintptr_t)window->id)); 2390 + ++wind->maximized_restored_deadline_count; 2391 + struct wl_callback *cb = wl_display_sync(viddata->display); 2392 + wl_callback_add_listener(cb, &maximized_restored_deadline_listener, (void *)((uintptr_t)window->id)); 2393 + } 2367 2394 } 2368 2395 2369 2396 void Wayland_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window) ··· 2651 2678 RepositionPopup(window, false); 2652 2679 return true; 2653 2680 } else if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR || wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) { 2654 - const bool use_pending_position_for_fullscreen = window->use_pending_position_for_fullscreen; 2655 - const int x = window->floating.x; 2656 - const int y = window->floating.y; 2657 - 2658 2681 /* Catch up on any pending state before attempting to change the fullscreen window 2659 2682 * display via a set fullscreen call to make sure the window doesn't have a pending 2660 2683 * leave fullscreen event that it might override. 2661 2684 */ 2662 2685 FlushPendingEvents(window); 2663 2686 2664 - /* XXX: Need to restore this after the roundtrip, as the requested coordinates might 2665 - * have been overwritten by the 'real' coordinates if a display enter/leave event 2666 - * occurred. 2667 - * 2668 - * The common pattern: 2669 - * 2670 - * SDL_SetWindowPosition(); 2671 - * SDL_SetWindowFullscreen(); 2672 - * 2673 - * for positioning a desktop fullscreen window won't work without this. 2674 - */ 2675 - window->use_pending_position_for_fullscreen = use_pending_position_for_fullscreen; 2676 - window->floating.x = x; 2677 - window->floating.y = y; 2678 - 2679 2687 if (wind->is_fullscreen) { 2680 2688 SDL_VideoDisplay *display = SDL_GetVideoDisplayForFullscreenWindow(window); 2681 2689 if (display && wind->last_displayID != display->id) { ··· 2693 2701 { 2694 2702 SDL_WindowData *wind = window->internal; 2695 2703 2696 - /* Fullscreen windows do not get explicitly resized, and not strictly 2697 - * obeying the size of maximized windows is a protocol violation, so 2698 - * it is necessary to flush any of these pending state operations. 2704 + /* Flush any pending state operations, as fullscreen windows do not get 2705 + * explicitly resized, not strictly obeying the size of a maximized window 2706 + * is a protocol violation, and pending restore events might result in a 2707 + * configure event overwriting the requested size. 2699 2708 * 2700 2709 * Calling this on a custom surface is informative, so the size must 2701 2710 * always be passed through. 2702 2711 */ 2703 2712 FlushPendingEvents(window); 2704 2713 2714 + // Maximized and fullscreen windows don't get resized. 2705 2715 if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) || 2706 2716 wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_CUSTOM) { 2707 2717 if (!wind->scale_to_display) { 2708 - wind->requested.logical_width = window->floating.w; 2709 - wind->requested.logical_height = window->floating.h; 2718 + wind->requested.logical_width = window->pending.w; 2719 + wind->requested.logical_height = window->pending.h; 2710 2720 } else { 2711 - wind->requested.logical_width = PixelToPoint(window, window->floating.w); 2712 - wind->requested.logical_height = PixelToPoint(window, window->floating.h); 2713 - wind->requested.pixel_width = window->floating.w; 2714 - wind->requested.pixel_height = window->floating.h; 2721 + wind->requested.logical_width = PixelToPoint(window, window->pending.w); 2722 + wind->requested.logical_height = PixelToPoint(window, window->pending.h); 2723 + wind->requested.pixel_width = window->pending.w; 2724 + wind->requested.pixel_height = window->pending.h; 2715 2725 } 2716 2726 2717 2727 ConfigureWindowGeometry(window); 2718 2728 } else { 2719 - wind->pending_restored_size = true; 2729 + // Can't resize the window. 2730 + window->last_size_pending = false; 2720 2731 } 2721 2732 2722 2733 // Always commit, as this may be in response to a min/max limit change. ··· 2833 2844 2834 2845 do { 2835 2846 WAYLAND_wl_display_roundtrip(_this->internal->display); 2836 - } while (wind->fullscreen_deadline_count || wind->maximized_deadline_count); 2847 + } while (wind->fullscreen_deadline_count || wind->maximized_restored_deadline_count); 2837 2848 2838 2849 return true; 2839 2850 }
+1 -2
src/video/wayland/SDL_waylandwindow.h
··· 176 176 177 177 SDL_DisplayID last_displayID; 178 178 int fullscreen_deadline_count; 179 - int maximized_deadline_count; 179 + int maximized_restored_deadline_count; 180 180 Uint64 last_focus_event_time_ns; 181 181 bool floating; 182 182 bool suspended; ··· 190 190 bool show_hide_sync_required; 191 191 bool scale_to_display; 192 192 bool reparenting_required; 193 - bool pending_restored_size; 194 193 bool double_buffer; 195 194 196 195 SDL_HitTestResult hit_test_result;
-26
src/video/windows/SDL_windowsevents.c
··· 1432 1432 break; 1433 1433 #endif // WM_GETMINMAXINFO 1434 1434 1435 - case WM_WINDOWPOSCHANGING: 1436 - { 1437 - if (data->expected_resize) { 1438 - returnCode = 0; 1439 - } 1440 - 1441 - if (data->floating_rect_pending && 1442 - !IsIconic(hwnd) && 1443 - !IsZoomed(hwnd) && 1444 - (data->window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED)) && 1445 - !(data->window->flags & SDL_WINDOW_FULLSCREEN)) { 1446 - // If a new floating size is pending, apply it if moving from a fixed-size to floating state. 1447 - WINDOWPOS *windowpos = (WINDOWPOS*)lParam; 1448 - int fx, fy, fw, fh; 1449 - 1450 - WIN_AdjustWindowRect(data->window, &fx, &fy, &fw, &fh, SDL_WINDOWRECT_FLOATING); 1451 - windowpos->x = fx; 1452 - windowpos->y = fy; 1453 - windowpos->cx = fw; 1454 - windowpos->cy = fh; 1455 - windowpos->flags &= ~(SWP_NOSIZE | SWP_NOMOVE); 1456 - 1457 - data->floating_rect_pending = false; 1458 - } 1459 - } break; 1460 - 1461 1435 case WM_WINDOWPOSCHANGED: 1462 1436 { 1463 1437 SDL_Window *win;
+10 -6
src/video/windows/SDL_windowswindow.c
··· 204 204 *width = window->floating.w; 205 205 *height = window->floating.h; 206 206 break; 207 + case SDL_WINDOWRECT_PENDING: 208 + SDL_RelativeToGlobalForWindow(window, window->pending.x, window->pending.y, x, y); 209 + *width = window->pending.w; 210 + *height = window->pending.h; 211 + break; 207 212 default: 208 213 // Should never be here 209 214 SDL_assert_release(false); ··· 901 906 if (!(window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) { 902 907 WIN_ConstrainPopup(window); 903 908 return WIN_SetWindowPositionInternal(window, 904 - window->internal->copybits_flag | SWP_NOZORDER | SWP_NOOWNERZORDER | 905 - SWP_NOACTIVATE, SDL_WINDOWRECT_FLOATING); 906 - } else { 907 - window->internal->floating_rect_pending = true; 909 + window->internal->copybits_flag | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSIZE | 910 + SWP_NOACTIVATE, SDL_WINDOWRECT_PENDING); 908 911 } 909 912 } else { 910 913 return SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_ENTER, true); ··· 916 919 void WIN_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) 917 920 { 918 921 if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED))) { 919 - WIN_SetWindowPositionInternal(window, window->internal->copybits_flag | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE, SDL_WINDOWRECT_FLOATING); 922 + WIN_SetWindowPositionInternal(window, window->internal->copybits_flag | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE, SDL_WINDOWRECT_PENDING); 920 923 } else { 921 - window->internal->floating_rect_pending = true; 924 + // Can't resize the window 925 + window->last_size_pending = false; 922 926 } 923 927 } 924 928
+2 -2
src/video/windows/SDL_windowswindow.h
··· 38 38 { 39 39 SDL_WINDOWRECT_CURRENT, 40 40 SDL_WINDOWRECT_WINDOWED, 41 - SDL_WINDOWRECT_FLOATING 41 + SDL_WINDOWRECT_FLOATING, 42 + SDL_WINDOWRECT_PENDING 42 43 } SDL_WindowRect; 43 44 44 45 typedef enum SDL_WindowEraseBackgroundMode ··· 76 77 bool expected_resize; 77 78 bool in_border_change; 78 79 bool in_title_click; 79 - bool floating_rect_pending; 80 80 Uint8 focus_click_pending; 81 81 bool skip_update_clipcursor; 82 82 Uint64 last_updated_clipcursor;
+36 -19
src/video/x11/SDL_x11events.c
··· 1772 1772 } 1773 1773 if (!(flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) { 1774 1774 data->pending_operation &= ~X11_PENDING_OP_RESTORE; 1775 - if (SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0)) { 1776 - // Restore the last known floating state if leaving maximized mode 1777 - if (!(flags & SDL_WINDOW_FULLSCREEN)) { 1778 - data->pending_operation |= X11_PENDING_OP_MOVE | X11_PENDING_OP_RESIZE; 1779 - data->expected.x = data->window->floating.x - data->border_left; 1780 - data->expected.y = data->window->floating.y - data->border_top; 1781 - data->expected.w = data->window->floating.w; 1782 - data->expected.h = data->window->floating.h; 1783 - X11_XMoveWindow(display, data->xwindow, data->window->floating.x - data->border_left, data->window->floating.y - data->border_top); 1784 - X11_XResizeWindow(display, data->xwindow, data->window->floating.w, data->window->floating.h); 1775 + SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0); 1776 + 1777 + // Apply any pending state if restored. 1778 + if (!(flags & SDL_WINDOW_FULLSCREEN)) { 1779 + if (data->pending_position) { 1780 + data->pending_position = false; 1781 + data->pending_operation |= X11_PENDING_OP_MOVE; 1782 + data->expected.x = data->window->pending.x - data->border_left; 1783 + data->expected.y = data->window->pending.y - data->border_top; 1784 + X11_XMoveWindow(display, data->xwindow, data->window->pending.x - data->border_left, data->window->pending.y - data->border_top); 1785 + } 1786 + if (data->pending_size) { 1787 + data->pending_size = false; 1788 + data->pending_operation |= X11_PENDING_OP_RESIZE; 1789 + data->expected.w = data->window->pending.w; 1790 + data->expected.h = data->window->pending.h; 1791 + X11_XResizeWindow(display, data->xwindow, data->window->pending.w, data->window->pending.h); 1785 1792 } 1786 1793 } 1787 1794 } ··· 1812 1819 X11_GetBorderValues(data); 1813 1820 if (data->border_top != 0 || data->border_left != 0 || data->border_right != 0 || data->border_bottom != 0) { 1814 1821 // Adjust if the window size/position changed to accommodate the borders. 1815 - if (data->window->flags & SDL_WINDOW_MAXIMIZED) { 1816 - data->pending_operation |= X11_PENDING_OP_RESIZE; 1822 + data->pending_operation |= X11_PENDING_OP_MOVE | X11_PENDING_OP_RESIZE; 1823 + 1824 + if (data->pending_position) { 1825 + data->pending_position = false; 1826 + data->expected.x = data->window->pending.x - data->border_left; 1827 + data->expected.y = data->window->pending.y - data->border_top; 1828 + 1829 + } else { 1830 + data->expected.x = data->window->windowed.x - data->border_left; 1831 + data->expected.y = data->window->windowed.y - data->border_top; 1832 + } 1833 + 1834 + if (data->pending_size) { 1835 + data->pending_size = false; 1836 + data->expected.w = data->window->pending.w; 1837 + data->expected.h = data->window->pending.h; 1838 + } else { 1817 1839 data->expected.w = data->window->windowed.w; 1818 1840 data->expected.h = data->window->windowed.h; 1819 - X11_XResizeWindow(display, data->xwindow, data->window->windowed.w, data->window->windowed.h); 1820 - } else { 1821 - data->pending_operation |= X11_PENDING_OP_RESIZE | X11_PENDING_OP_MOVE; 1822 - data->expected.w = data->window->floating.w; 1823 - data->expected.h = data->window->floating.h; 1824 - X11_XMoveWindow(display, data->xwindow, data->window->floating.x - data->border_left, data->window->floating.y - data->border_top); 1825 - X11_XResizeWindow(display, data->xwindow, data->window->floating.w, data->window->floating.h); 1826 1841 } 1842 + X11_XMoveWindow(display, data->xwindow, data->expected.x, data->expected.y - data->border_top); 1843 + X11_XResizeWindow(display, data->xwindow, data->expected.w, data->expected.h); 1827 1844 } 1828 1845 } 1829 1846 if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) {
+64 -35
src/video/x11/SDL_x11window.c
··· 126 126 } 127 127 #endif // 0 128 128 129 + static void X11_FlushPendingEvents(SDL_VideoDevice *_this, SDL_Window *window) 130 + { 131 + // Serialize and restore the pending flags, as they may be overwritten while flushing. 132 + const bool last_position_pending = window->last_position_pending; 133 + const bool last_size_pending = window->last_size_pending; 134 + 135 + X11_SyncWindow(_this, window); 136 + 137 + window->last_position_pending = last_position_pending; 138 + window->last_size_pending = last_size_pending; 139 + } 140 + 129 141 void X11_SetNetWMState(SDL_VideoDevice *_this, Window xwindow, SDL_WindowFlags flags) 130 142 { 131 143 SDL_VideoData *videodata = _this->internal; ··· 184 196 } 185 197 } 186 198 187 - static void X11_ConstrainPopup(SDL_Window *window) 199 + static void X11_ConstrainPopup(SDL_Window *window, bool use_current_position) 188 200 { 189 201 // Clamp popup windows to the output borders 190 202 if (SDL_WINDOW_IS_POPUP(window)) { 191 203 SDL_Window *w; 192 204 SDL_DisplayID displayID; 193 205 SDL_Rect rect; 194 - int abs_x = window->floating.x; 195 - int abs_y = window->floating.y; 206 + int abs_x = use_current_position ? window->floating.x : window->pending.x; 207 + int abs_y = use_current_position ? window->floating.y : window->pending.y; 196 208 int offset_x = 0, offset_y = 0; 197 209 198 210 // Calculate the total offset from the parents ··· 673 685 } 674 686 675 687 if (SDL_WINDOW_IS_POPUP(window)) { 676 - X11_ConstrainPopup(window); 688 + X11_ConstrainPopup(window, true); 677 689 } 678 690 SDL_RelativeToGlobalForWindow(window, 679 691 window->floating.x, window->floating.y, ··· 1044 1056 { 1045 1057 SDL_WindowData *data = window->internal; 1046 1058 Display *display = data->videodata->display; 1047 - const int rel_x = use_current_position ? window->x : window->floating.x; 1048 - const int rel_y = use_current_position ? window->y : window->floating.y; 1059 + const int rel_x = use_current_position ? window->x : window->pending.x; 1060 + const int rel_y = use_current_position ? window->y : window->pending.y; 1049 1061 1050 1062 SDL_RelativeToGlobalForWindow(window, 1051 1063 rel_x - data->border_left, rel_y - data->border_top, ··· 1060 1072 { 1061 1073 // Sync any pending fullscreen or maximize events. 1062 1074 if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE)) { 1063 - // Save state in case it is overwritten while synchronizing. 1064 - const bool use_client_fs_coords = window->use_pending_position_for_fullscreen; 1065 - const int x = window->floating.x; 1066 - const int y = window->floating.y; 1067 - 1068 - X11_SyncWindow(_this, window); 1069 - 1070 - // Restore state that may have been overwritten while synchronizing. 1071 - window->use_pending_position_for_fullscreen = use_client_fs_coords; 1072 - window->floating.x = x; 1073 - window->floating.y = y; 1075 + X11_FlushPendingEvents(_this, window); 1074 1076 } 1075 1077 1076 - // Position will be set when window is de-maximized 1078 + // Set the position as pending if the window is maximized with a restore pending. 1077 1079 if (window->flags & SDL_WINDOW_MAXIMIZED) { 1080 + if (window->internal->pending_operation & X11_PENDING_OP_RESTORE) { 1081 + window->internal->pending_position = true; 1082 + } 1078 1083 return true; 1079 1084 } 1080 1085 1081 1086 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 1082 1087 if (SDL_WINDOW_IS_POPUP(window)) { 1083 - X11_ConstrainPopup(window); 1088 + X11_ConstrainPopup(window, false); 1084 1089 } 1085 1090 X11_UpdateWindowPosition(window, false); 1086 1091 } else { ··· 1113 1118 hide/show, because there are supposedly subtle problems with doing so 1114 1119 and transitioning from windowed to fullscreen in Unity. 1115 1120 */ 1116 - X11_XResizeWindow(display, data->xwindow, window->floating.w, window->floating.h); 1121 + X11_XResizeWindow(display, data->xwindow, window->pending.w, window->pending.h); 1117 1122 SDL_RelativeToGlobalForWindow(window, 1118 - window->floating.x - data->border_left, 1119 - window->floating.y - data->border_top, 1123 + window->pending.x - data->border_left, 1124 + window->pending.y - data->border_top, 1120 1125 &dest_x, &dest_y); 1121 1126 X11_XMoveWindow(display, data->xwindow, dest_x, dest_y); 1122 1127 X11_XRaiseWindow(display, data->xwindow); ··· 1197 1202 SDL_WindowData *data = window->internal; 1198 1203 Display *display = data->videodata->display; 1199 1204 1200 - /* Wait for pending maximize operations to complete, or the window can end up in a weird, 1201 - * partially-maximized state. 1205 + /* Wait for pending maximize and fullscreen operations to complete, as these windows 1206 + * don't get size changes. 1202 1207 */ 1203 1208 if (data->pending_operation & (X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_FULLSCREEN)) { 1204 - X11_SyncWindow(_this, window); 1209 + X11_FlushPendingEvents(_this, window); 1205 1210 } 1206 1211 1207 - // Don't try to resize a maximized or fullscreen window, it will be done on restore. 1212 + // Set the size as pending if the window is being restored. 1208 1213 if (window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_FULLSCREEN)) { 1214 + // New size will be set when the window is restored. 1215 + if (data->pending_operation & X11_PENDING_OP_RESTORE) { 1216 + data->pending_size = true; 1217 + } else { 1218 + // Can't resize the window. 1219 + window->last_size_pending = false; 1220 + } 1209 1221 return; 1210 1222 } 1211 1223 ··· 1219 1231 1220 1232 X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints); 1221 1233 1222 - sizehints->min_width = sizehints->max_width = window->floating.w; 1223 - sizehints->min_height = sizehints->max_height = window->floating.h; 1234 + sizehints->min_width = sizehints->max_width = window->pending.w; 1235 + sizehints->min_height = sizehints->max_height = window->pending.h; 1224 1236 sizehints->flags |= PMinSize | PMaxSize; 1225 1237 1226 1238 X11_SetWMNormalHints(_this, window, sizehints); ··· 1228 1240 X11_XFree(sizehints); 1229 1241 } 1230 1242 } else { 1231 - data->expected.w = window->floating.w; 1232 - data->expected.h = window->floating.h; 1243 + data->expected.w = window->pending.w; 1244 + data->expected.h = window->pending.h; 1233 1245 data->pending_operation |= X11_PENDING_OP_RESIZE; 1234 1246 X11_XResizeWindow(display, data->xwindow, data->expected.w, data->expected.h); 1235 1247 } ··· 1577 1589 Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT; 1578 1590 Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ; 1579 1591 1580 - if (!maximized && window->flags & SDL_WINDOW_FULLSCREEN) { 1592 + if (window->flags & SDL_WINDOW_FULLSCREEN) { 1581 1593 /* Fullscreen windows are maximized on some window managers, 1582 1594 and this is functional behavior, so don't remove that state 1583 1595 now, we'll take care of it when we leave fullscreen mode. ··· 1633 1645 SDL_SyncWindow(window); 1634 1646 } 1635 1647 1636 - if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MINIMIZED))) { 1648 + if (window->flags & SDL_WINDOW_FULLSCREEN) { 1649 + // If fullscreen, just toggle the restored state. 1650 + window->internal->window_was_maximized = true; 1651 + return; 1652 + } 1653 + 1654 + if (!(window->flags & SDL_WINDOW_MINIMIZED)) { 1637 1655 window->internal->pending_operation |= X11_PENDING_OP_MAXIMIZE; 1638 1656 X11_SetWindowMaximized(_this, window, true); 1639 1657 } ··· 1645 1663 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); 1646 1664 Display *display = data->videodata->display; 1647 1665 1666 + if (data->pending_operation & SDL_WINDOW_FULLSCREEN) { 1667 + SDL_SyncWindow(window); 1668 + } 1669 + 1648 1670 data->pending_operation |= X11_PENDING_OP_MINIMIZE; 1649 - data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED); 1671 + if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 1672 + data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED); 1673 + } 1650 1674 X11_XIconifyWindow(display, data->xwindow, displaydata->screen); 1651 1675 X11_XFlush(display); 1652 1676 } ··· 1657 1681 SDL_SyncWindow(window); 1658 1682 } 1659 1683 1684 + if ((window->flags & SDL_WINDOW_FULLSCREEN) && !(window->flags & SDL_WINDOW_MINIMIZED)) { 1685 + // If fullscreen and not minimized, just toggle the restored state. 1686 + window->internal->window_was_maximized = false; 1687 + return; 1688 + } 1689 + 1660 1690 if (window->flags & (SDL_WINDOW_MINIMIZED | SDL_WINDOW_MAXIMIZED) || 1661 1691 (window->internal->pending_operation & X11_PENDING_OP_MINIMIZE)) { 1662 1692 window->internal->pending_operation |= X11_PENDING_OP_RESTORE; 1663 1693 } 1664 1694 1665 1695 // If the window was minimized while maximized, restore as maximized. 1666 - const bool maximize = !!(window->flags & SDL_WINDOW_MINIMIZED) && window->internal->window_was_maximized; 1667 - window->internal->window_was_maximized = false; 1696 + const bool maximize = !!(window->flags & SDL_WINDOW_MINIMIZED) && window->internal->window_was_maximized; 1668 1697 X11_SetWindowMaximized(_this, window, maximize); 1669 1698 X11_ShowWindow(_this, window); 1670 1699 X11_SetWindowActive(_this, window);
+2
src/video/x11/SDL_x11window.h
··· 103 103 X11_PENDING_OP_RESIZE = 0x20 104 104 } pending_operation; 105 105 106 + bool pending_size; 107 + bool pending_position; 106 108 bool window_was_maximized; 107 109 bool disable_size_position_events; 108 110 bool previous_borders_nonzero;
+52 -5
test/testautomation_video.c
··· 2133 2133 SDLTest_AssertCheck(windowedW == currentW, "Verify returned width; expected: %d, got: %d", windowedW, currentW); 2134 2134 SDLTest_AssertCheck(windowedH == currentH, "Verify returned height; expected: %d, got: %d", windowedH, currentH); 2135 2135 2136 - /* Maximize, change size, and restore */ 2136 + /* Maximize, restore, and change size */ 2137 2137 result = SDL_MaximizeWindow(window); 2138 2138 SDLTest_AssertPass("SDL_MaximizeWindow()"); 2139 + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); 2140 + 2141 + result = SDL_RestoreWindow(window); 2142 + SDLTest_AssertPass("SDL_RestoreWindow()"); 2139 2143 SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); 2140 2144 2141 2145 desiredW = windowedW + 10; ··· 2152 2156 SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); 2153 2157 } 2154 2158 2155 - result = SDL_RestoreWindow(window); 2156 - SDLTest_AssertPass("SDL_RestoreWindow()"); 2157 - SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); 2158 - 2159 2159 result = SDL_SyncWindow(window); 2160 2160 SDLTest_AssertPass("SDL_SyncWindow()"); 2161 2161 SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); ··· 2179 2179 SDLTest_AssertPass("Call to SDL_GetWindowSize()"); 2180 2180 SDLTest_AssertCheck(desiredW == currentW, "Verify returned width; expected: %d, got: %d", desiredW, currentW); 2181 2181 SDLTest_AssertCheck(desiredH == currentH, "Verify returned height; expected: %d, got: %d", desiredH, currentH); 2182 + 2183 + /* Maximize, change size/position (should be ignored), and restore. */ 2184 + result = SDL_MaximizeWindow(window); 2185 + SDLTest_AssertPass("SDL_MaximizeWindow()"); 2186 + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); 2187 + 2188 + desiredW = windowedW + 10; 2189 + desiredH = windowedH + 10; 2190 + result = SDL_SetWindowSize(window, desiredW, desiredH); 2191 + SDLTest_AssertPass("SDL_SetWindowSize()"); 2192 + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); 2193 + 2194 + if (!skipPos) { 2195 + desiredX = windowedX + 10; 2196 + desiredY = windowedY + 10; 2197 + result = SDL_SetWindowPosition(window, desiredX, desiredY); 2198 + SDLTest_AssertPass("SDL_SetWindowPosition()"); 2199 + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); 2200 + } 2201 + 2202 + result = SDL_RestoreWindow(window); 2203 + SDLTest_AssertPass("SDL_RestoreWindow()"); 2204 + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); 2205 + 2206 + result = SDL_SyncWindow(window); 2207 + SDLTest_AssertPass("SDL_SyncWindow()"); 2208 + SDLTest_AssertCheck(result == true, "Verify return value; expected: true, got: %d", result); 2209 + 2210 + flags = SDL_GetWindowFlags(window); 2211 + SDLTest_AssertPass("SDL_GetWindowFlags()"); 2212 + SDLTest_AssertCheck(!(flags & SDL_WINDOW_MAXIMIZED), "Verify that the `SDL_WINDOW_MAXIMIZED` flag is cleared: %s", !(flags & SDL_WINDOW_MAXIMIZED) ? "true" : "false"); 2213 + 2214 + if (!skipPos) { 2215 + int previousX = desiredX + 1; 2216 + int previousY = desiredY + 1; 2217 + SDL_GetWindowPosition(window, &previousX, &previousY); 2218 + SDLTest_AssertPass("Call to SDL_GetWindowPosition()"); 2219 + SDLTest_AssertCheck(desiredX == currentX, "Verify returned X coordinate; expected: %d, got: %d", previousX, currentX); 2220 + SDLTest_AssertCheck(desiredY == currentY, "Verify returned Y coordinate; expected: %d, got: %d", previousY, currentY); 2221 + } 2222 + 2223 + int previousW = desiredW + 1; 2224 + int previousH = desiredH + 1; 2225 + SDL_GetWindowSize(window, &previousW, &previousH); 2226 + SDLTest_AssertPass("Call to SDL_GetWindowSize()"); 2227 + SDLTest_AssertCheck(desiredW == currentW, "Verify returned width; expected: %d, got: %d", previousW, currentW); 2228 + SDLTest_AssertCheck(desiredH == currentH, "Verify returned height; expected: %d, got: %d", previousH, currentH); 2182 2229 2183 2230 /* Change size and position, maximize and restore */ 2184 2231 desiredW = windowedW - 5;