mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-05-04 07:10:15 +02:00
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.
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user