x11: Fix initial window positioning
Some window managers can send garbage values during the initial mapping of a window, and need the position set again after mapping to ensure proper placement. Position requests sent before mapping can otherwise end up ignored. Ignore initial configure events when initially showing the window, and make sure that the position is set after the window is mapped, either when the window borders appear, or after the initial configure events in the case of borderless windows. This also eliminates sending excessive/redundant move requests, which can cause strange behavior on some window managers, particularly if done before the window is actually mapped. Fixes cases of incorrect initial window placement on GNOME + XWayland.main
parent
01195c5d32
commit
ad813a65e7
|
@ -498,6 +498,7 @@ extern void SDL_SetDisplayContentScale(SDL_VideoDisplay *display, float scale);
|
|||
extern void SDL_SetDisplayHDRProperties(SDL_VideoDisplay *display, const SDL_HDRDisplayProperties *HDR);
|
||||
extern int SDL_SetDisplayModeForDisplay(SDL_VideoDisplay *display, SDL_DisplayMode *mode);
|
||||
extern SDL_VideoDisplay *SDL_GetVideoDisplay(SDL_DisplayID display);
|
||||
extern SDL_DisplayID SDL_GetDisplayForWindowPosition(SDL_Window *window);
|
||||
extern SDL_VideoDisplay *SDL_GetVideoDisplayForWindow(SDL_Window *window);
|
||||
extern SDL_VideoDisplay *SDL_GetVideoDisplayForFullscreenWindow(SDL_Window *window);
|
||||
extern int SDL_GetDisplayIndex(SDL_DisplayID displayID);
|
||||
|
|
|
@ -1388,7 +1388,7 @@ SDL_DisplayID SDL_GetDisplayForRect(const SDL_Rect *rect)
|
|||
return GetDisplayForRect(rect->x, rect->y, rect->w, rect->h);
|
||||
}
|
||||
|
||||
static SDL_DisplayID SDL_GetDisplayForWindowPosition(SDL_Window *window)
|
||||
SDL_DisplayID SDL_GetDisplayForWindowPosition(SDL_Window *window)
|
||||
{
|
||||
int x, y;
|
||||
SDL_DisplayID displayID = 0;
|
||||
|
|
|
@ -1687,8 +1687,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
|
|||
|
||||
if (flags & SDL_WINDOW_FULLSCREEN) {
|
||||
if (!(flags & SDL_WINDOW_MINIMIZED)) {
|
||||
const SDL_bool commit = data->requested_fullscreen_mode.displayID == 0 ||
|
||||
SDL_memcmp(&data->window->current_fullscreen_mode, &data->requested_fullscreen_mode, sizeof(SDL_DisplayMode)) != 0;
|
||||
const SDL_bool commit = SDL_memcmp(&data->window->current_fullscreen_mode, &data->requested_fullscreen_mode, sizeof(SDL_DisplayMode)) != 0;
|
||||
|
||||
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0);
|
||||
if (commit) {
|
||||
|
@ -1705,6 +1704,8 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
|
|||
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0);
|
||||
SDL_UpdateFullscreenMode(data->window, SDL_FALSE, SDL_FALSE);
|
||||
|
||||
SDL_zero(data->requested_fullscreen_mode);
|
||||
|
||||
/* Need to restore or update any limits changed while the window was fullscreen. */
|
||||
X11_SetWindowMinMax(data->window, !!(flags & SDL_WINDOW_MAXIMIZED));
|
||||
}
|
||||
|
@ -1753,8 +1754,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
|
|||
}
|
||||
if (!(flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) {
|
||||
data->pending_operation &= ~X11_PENDING_OP_RESTORE;
|
||||
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
|
||||
|
||||
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;
|
||||
|
@ -1766,6 +1766,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
|
|||
X11_XResizeWindow(display, data->xwindow, data->window->floating.w, data->window->floating.h);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((flags & SDL_WINDOW_INPUT_FOCUS)) {
|
||||
if (data->pending_move) {
|
||||
DispatchWindowMove(_this, data, &data->pending_move_point);
|
||||
|
@ -1785,6 +1786,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
|
|||
right approach, but it seems to work. */
|
||||
X11_UpdateKeymap(_this, SDL_TRUE);
|
||||
} else if (xevent->xproperty.atom == videodata->_NET_FRAME_EXTENTS) {
|
||||
if (data->disable_size_position_events) {
|
||||
/* Re-enable size events if they were turned off waiting for the borders to come back
|
||||
* when leaving fullscreen.
|
||||
*/
|
||||
|
@ -1805,6 +1807,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
|
|||
X11_XResizeWindow(display, data->xwindow, data->window->floating.w, data->window->floating.h);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) {
|
||||
data->toggle_borders = SDL_FALSE;
|
||||
X11_SetWindowBordered(_this, data->window, !(data->window->flags & SDL_WINDOW_BORDERLESS));
|
||||
|
|
|
@ -895,7 +895,7 @@ static int X11_SyncWindowTimeout(SDL_VideoDevice *_this, SDL_Window *window, int
|
|||
X11_XSync(display, False);
|
||||
X11_PumpEvents(_this);
|
||||
|
||||
if ((data->pending_operation & X11_PENDING_OP_MOVE) && (window->x == data->expected.x && window->y == data->expected.y)) {
|
||||
if ((data->pending_operation & X11_PENDING_OP_MOVE) && (window->x == data->expected.x + data->border_left && window->y == data->expected.y + data->border_top)) {
|
||||
data->pending_operation &= ~X11_PENDING_OP_MOVE;
|
||||
}
|
||||
if ((data->pending_operation & X11_PENDING_OP_RESIZE) && (window->w == data->expected.w && window->h == data->expected.h)) {
|
||||
|
@ -904,7 +904,7 @@ static int X11_SyncWindowTimeout(SDL_VideoDevice *_this, SDL_Window *window, int
|
|||
|
||||
if (data->pending_operation == X11_PENDING_OP_NONE) {
|
||||
if (force_exit ||
|
||||
(window->x == data->expected.x && window->y == data->expected.y &&
|
||||
(window->x == data->expected.x + data->border_left && window->y == data->expected.y + data->border_top &&
|
||||
window->w == data->expected.w && window->h == data->expected.h)) {
|
||||
/* The window is in the expected state and nothing is pending. Done. */
|
||||
break;
|
||||
|
@ -1358,6 +1358,29 @@ void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
|||
if (data->border_left == 0 && data->border_right == 0 && data->border_top == 0 && data->border_bottom == 0) {
|
||||
X11_GetBorderValues(data);
|
||||
}
|
||||
|
||||
/* Some window managers can send garbage coordinates while mapping the window, and need the position sent again
|
||||
* after mapping or the window may not be positioned properly.
|
||||
*
|
||||
* Don't emit size and position events during the initial configure events, they will be sent afterwards, when the
|
||||
* final coordinates are available to avoid sending garbage values.
|
||||
*/
|
||||
data->disable_size_position_events = SDL_TRUE;
|
||||
X11_XSync(display, False);
|
||||
X11_PumpEvents(_this);
|
||||
|
||||
int x = data->last_xconfigure.x;
|
||||
int y = data->last_xconfigure.y;
|
||||
SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y);
|
||||
|
||||
/* If the borders appeared, this happened automatically in the event system, otherwise, set the position now. */
|
||||
if (data->disable_size_position_events && (window->x != x || window->y != y)) {
|
||||
data->pending_operation = X11_PENDING_OP_MOVE;
|
||||
X11_XMoveWindow(display, data->xwindow, window->x, window->y);
|
||||
}
|
||||
data->disable_size_position_events = SDL_FALSE;
|
||||
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, data->last_xconfigure.width, data->last_xconfigure.height);
|
||||
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y);
|
||||
}
|
||||
|
||||
void X11_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
|
@ -1551,7 +1574,7 @@ static int X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, SDL_Window *wind
|
|||
XEvent e;
|
||||
|
||||
/* Flush any pending fullscreen events. */
|
||||
if (data->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE)) {
|
||||
if (data->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_MOVE)) {
|
||||
X11_SyncWindow(_this, window);
|
||||
}
|
||||
|
||||
|
@ -1591,10 +1614,8 @@ static int X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, SDL_Window *wind
|
|||
|
||||
/* Set the position so the window will be on the target display */
|
||||
if (fullscreen) {
|
||||
SDL_DisplayID current = SDL_GetDisplayForWindowPosition(window);
|
||||
SDL_copyp(&data->requested_fullscreen_mode, &window->current_fullscreen_mode);
|
||||
if (data->requested_fullscreen_mode.displayID == 0) {
|
||||
data->requested_fullscreen_mode.displayID = _display->id;
|
||||
}
|
||||
if (fullscreen != !!(window->flags & SDL_WINDOW_FULLSCREEN)) {
|
||||
data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED);
|
||||
}
|
||||
|
@ -1602,7 +1623,12 @@ static int X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, SDL_Window *wind
|
|||
data->expected.y = displaydata->y;
|
||||
data->expected.w = _display->current_mode->w;
|
||||
data->expected.h = _display->current_mode->h;
|
||||
|
||||
/* Only move the window if it isn't fullscreen or already on the target display. */
|
||||
if (!(window->flags & SDL_WINDOW_FULLSCREEN) || (!current || current != _display->id)) {
|
||||
X11_XMoveWindow(display, data->xwindow, displaydata->x, displaydata->y);
|
||||
data->pending_operation |= X11_PENDING_OP_MOVE;
|
||||
}
|
||||
} else {
|
||||
SDL_zero(data->requested_fullscreen_mode);
|
||||
|
||||
|
|
Loading…
Reference in New Issue