From 3b4472ecf767d1da2d41b014d2306bd7abb32c92 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Thu, 24 Apr 2025 11:22:25 -0400
Subject: [PATCH] x11: Handle size/position events arriving before state events
Xfce, unlike every other window manager in existence, sends ConfigureNotify events before PropertyNotify events when toggling the fullscreen and maximized window state. Check the window state when handling ConfigureNotify events, and defer emitting SDL size/position events until the corresponding PropertyNotify event arrives, since SDL and clients expect to get the window state before the new size and position.
(cherry picked from commit 11a3296a42594da107ef3913c6bb6df8b1d63f4d)
---
src/video/x11/SDL_x11events.c | 84 ++++++++++++++++++++++-------------
src/video/x11/SDL_x11window.h | 2 +
2 files changed, 54 insertions(+), 32 deletions(-)
diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c
index 8f0ecc9b1dbf6..dcc6c878c6ed9 100644
--- a/src/video/x11/SDL_x11events.c
+++ b/src/video/x11/SDL_x11events.c
@@ -1108,6 +1108,41 @@ void X11_GetBorderValues(SDL_WindowData *data)
}
}
+void X11_EmitConfigureNotifyEvents(SDL_WindowData *data, XConfigureEvent *xevent)
+{
+ if (xevent->x != data->last_xconfigure.x ||
+ xevent->y != data->last_xconfigure.y) {
+ if (!data->size_move_event_flags) {
+ SDL_Window *w;
+ int x = xevent->x;
+ int y = xevent->y;
+
+ data->pending_operation &= ~X11_PENDING_OP_MOVE;
+ SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y);
+ SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y);
+
+ for (w = data->window->first_child; w; w = w->next_sibling) {
+ // Don't update hidden child popup windows, their relative position doesn't change
+ if (SDL_WINDOW_IS_POPUP(w) && !(w->flags & SDL_WINDOW_HIDDEN)) {
+ X11_UpdateWindowPosition(w, true);
+ }
+ }
+ }
+ }
+
+ if (xevent->width != data->last_xconfigure.width ||
+ xevent->height != data->last_xconfigure.height) {
+ if (!data->size_move_event_flags) {
+ data->pending_operation &= ~X11_PENDING_OP_RESIZE;
+ SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED,
+ xevent->width,
+ xevent->height);
+ }
+ }
+
+ SDL_copyp(&data->last_xconfigure, xevent);
+}
+
static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
{
SDL_VideoData *videodata = _this->internal;
@@ -1459,9 +1494,8 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
xevent->xconfigure.x, xevent->xconfigure.y,
xevent->xconfigure.width, xevent->xconfigure.height);
#endif
- // Real configure notify events are relative to the parent, synthetic events are absolute.
- if (!xevent->xconfigure.send_event)
- {
+ // Real configure notify events are relative to the parent, synthetic events are absolute.
+ if (!xevent->xconfigure.send_event) {
unsigned int NumChildren;
Window ChildReturn, Root, Parent;
Window *Children;
@@ -1474,41 +1508,23 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
&ChildReturn);
}
- if (xevent->xconfigure.x != data->last_xconfigure.x ||
- xevent->xconfigure.y != data->last_xconfigure.y) {
- if (!data->size_move_event_flags) {
- SDL_Window *w;
- int x = xevent->xconfigure.x;
- int y = xevent->xconfigure.y;
-
- data->pending_operation &= ~X11_PENDING_OP_MOVE;
- SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y);
- SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y);
+ /* Xfce sends ConfigureNotify before PropertyNotify when toggling fullscreen and maximized, which
+ * is backwards from every other window manager, as well as what is expected by SDL and its clients.
+ * Defer emitting the size/move events until the corresponding PropertyNotify arrives.
+ */
+ const Uint32 changed = X11_GetNetWMState(_this, data->window, xevent->xproperty.window) ^ data->window->flags;
+ if (changed & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) {
+ SDL_copyp(&data->pending_xconfigure, &xevent->xconfigure);
+ data->emit_size_move_after_property_notify = true;
+ }
- for (w = data->window->first_child; w; w = w->next_sibling) {
- // Don't update hidden child popup windows, their relative position doesn't change
- if (SDL_WINDOW_IS_POPUP(w) && !(w->flags & SDL_WINDOW_HIDDEN)) {
- X11_UpdateWindowPosition(w, true);
- }
- }
- }
+ if (!data->emit_size_move_after_property_notify) {
+ X11_EmitConfigureNotifyEvents(data, &xevent->xconfigure);
}
#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
X11_HandleConfigure(data->window, &xevent->xconfigure);
#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
-
- if (xevent->xconfigure.width != data->last_xconfigure.width ||
- xevent->xconfigure.height != data->last_xconfigure.height) {
- if (!data->size_move_event_flags) {
- data->pending_operation &= ~X11_PENDING_OP_RESIZE;
- SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED,
- xevent->xconfigure.width,
- xevent->xconfigure.height);
- }
- }
-
- data->last_xconfigure = xevent->xconfigure;
} break;
// Have we been requested to quit (or another client message?)
@@ -1906,6 +1922,10 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
}
}
}
+ if (data->emit_size_move_after_property_notify) {
+ X11_EmitConfigureNotifyEvents(data, &data->pending_xconfigure);
+ data->emit_size_move_after_property_notify = false;
+ }
if ((flags & SDL_WINDOW_INPUT_FOCUS)) {
if (data->pending_move) {
DispatchWindowMove(_this, data, &data->pending_move_point);
diff --git a/src/video/x11/SDL_x11window.h b/src/video/x11/SDL_x11window.h
index 2d7b3239d37a1..ce90ed3061ef2 100644
--- a/src/video/x11/SDL_x11window.h
+++ b/src/video/x11/SDL_x11window.h
@@ -68,6 +68,7 @@ struct SDL_WindowData
bool pending_move;
SDL_Point pending_move_point;
XConfigureEvent last_xconfigure;
+ XConfigureEvent pending_xconfigure;
struct SDL_VideoData *videodata;
unsigned long user_time;
Atom xdnd_req;
@@ -116,6 +117,7 @@ struct SDL_WindowData
bool toggle_borders;
bool fullscreen_borders_forced_on;
bool was_shown;
+ bool emit_size_move_after_property_notify;
SDL_HitTestResult hit_test_result;
XPoint xim_spot;