SDL: X11: fix size/position (test video_setWindowCenteredOnDisplay)

From 4b1378fe8e3f72b67e98510db20b8057baf2d2b9 Mon Sep 17 00:00:00 2001
From: Sylvain <[EMAIL REDACTED]>
Date: Sat, 18 Mar 2023 16:07:11 +0100
Subject: [PATCH] X11: fix size/position (test
 video_setWindowCenteredOnDisplay) this fix x11 backend to correctly pass
 video_setWindowCenteredOnDisplay() - get border values early  (eg status bar)
 - wait for size/position change to get valid values

Fix set topleft is client rendering area
---
 src/video/x11/SDL_x11events.c | 42 +++++++++++++++++++++--------------
 src/video/x11/SDL_x11events.h |  1 +
 src/video/x11/SDL_x11window.c | 35 ++++++++++++++++++++++++-----
 3 files changed, 55 insertions(+), 23 deletions(-)

diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c
index b373be12f376..fa5a34cd97e6 100644
--- a/src/video/x11/SDL_x11events.c
+++ b/src/video/x11/SDL_x11events.c
@@ -754,6 +754,30 @@ static int XLookupStringAsUTF8(XKeyEvent *event_struct, char *buffer_return, int
     return result;
 }
 
+void X11_GetBorderValues(SDL_WindowData *data)
+{
+    SDL_VideoData *videodata = data->videodata;
+    Display *display = videodata->display;
+
+    Atom type;
+    int format;
+    unsigned long nitems, bytes_after;
+    unsigned char *property;
+    if (X11_XGetWindowProperty(display, data->xwindow, videodata->_NET_FRAME_EXTENTS, 0, 16, 0, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &property) == Success) {
+        if (type != None && nitems == 4) {
+            data->border_left = (int)((long *)property)[0];
+            data->border_right = (int)((long *)property)[1];
+            data->border_top = (int)((long *)property)[2];
+            data->border_bottom = (int)((long *)property)[3];
+        }
+        X11_XFree(property);
+
+#ifdef DEBUG_XEVENTS
+        printf("New _NET_FRAME_EXTENTS: left=%d right=%d, top=%d, bottom=%d\n", data->border_left, data->border_right, data->border_top, data->border_bottom);
+#endif
+    }
+}
+
 static void X11_DispatchEvent(_THIS, XEvent *xevent)
 {
     SDL_VideoData *videodata = _this->driverdata;
@@ -1513,23 +1537,7 @@ static void X11_DispatchEvent(_THIS, XEvent *xevent)
                right approach, but it seems to work. */
             X11_UpdateKeymap(_this, SDL_TRUE);
         } else if (xevent->xproperty.atom == videodata->_NET_FRAME_EXTENTS) {
-            Atom type;
-            int format;
-            unsigned long nitems, bytes_after;
-            unsigned char *property;
-            if (X11_XGetWindowProperty(display, data->xwindow, videodata->_NET_FRAME_EXTENTS, 0, 16, 0, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &property) == Success) {
-                if (type != None && nitems == 4) {
-                    data->border_left = (int)((long *)property)[0];
-                    data->border_right = (int)((long *)property)[1];
-                    data->border_top = (int)((long *)property)[2];
-                    data->border_bottom = (int)((long *)property)[3];
-                }
-                X11_XFree(property);
-
-#ifdef DEBUG_XEVENTS
-                printf("New _NET_FRAME_EXTENTS: left=%d right=%d, top=%d, bottom=%d\n", data->border_left, data->border_right, data->border_top, data->border_bottom);
-#endif
-            }
+            X11_GetBorderValues(data);
         }
     } break;
 
diff --git a/src/video/x11/SDL_x11events.h b/src/video/x11/SDL_x11events.h
index a95b58454d49..55b204b32c64 100644
--- a/src/video/x11/SDL_x11events.h
+++ b/src/video/x11/SDL_x11events.h
@@ -28,5 +28,6 @@ extern int X11_WaitEventTimeout(_THIS, Sint64 timeoutNS);
 extern void X11_SendWakeupEvent(_THIS, SDL_Window *window);
 extern int X11_SuspendScreenSaver(_THIS);
 extern void X11_ReconcileKeyboardState(_THIS);
+extern void X11_GetBorderValues(SDL_WindowData *data);
 
 #endif /* SDL_x11events_h_ */
diff --git a/src/video/x11/SDL_x11window.c b/src/video/x11/SDL_x11window.c
index 99f84e5a4959..3fb5ca1b6f92 100644
--- a/src/video/x11/SDL_x11window.c
+++ b/src/video/x11/SDL_x11window.c
@@ -905,8 +905,8 @@ void X11_UpdateWindowPosition(SDL_Window *window)
                               attrs.x, attrs.y, &orig_x, &orig_y, &childReturn);
 
     SDL_RelativeToGlobalForWindow(window,
-                                      window->x - data->border_left, window->y - data->border_top,
-                                      &dest_x, &dest_y);
+                                  window->x - data->border_left, window->y - data->border_top,
+                                  &dest_x, &dest_y);
 
     /* Attempt to move the window */
     X11_XMoveWindow(display, data->xwindow, dest_x, dest_y);
@@ -1292,6 +1292,12 @@ void X11_ShowWindow(_THIS, SDL_Window *window)
             X11_SetKeyboardFocus(window);
         }
     }
+
+    /* Get some valid border values, if we haven't them yet */
+    if (data->border_left == 0 && data->border_right == 0 && data->border_top == 0 && data->border_bottom == 0) {
+        X11_GetBorderValues(data);
+    }
+
 }
 
 void X11_HideWindow(_THIS, SDL_Window *window)
@@ -1438,6 +1444,8 @@ static void X11_SetWindowFullscreenViaWM(_THIS, SDL_Window *window, SDL_VideoDis
     Display *display = data->videodata->display;
     Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE;
     Atom _NET_WM_STATE_FULLSCREEN = data->videodata->_NET_WM_STATE_FULLSCREEN;
+    SDL_bool window_size_changed = SDL_FALSE;
+    int window_position_changed = 0;
 
     if (X11_IsWindowMapped(_this, window)) {
         XEvent e;
@@ -1516,6 +1524,17 @@ static void X11_SetWindowFullscreenViaWM(_THIS, SDL_Window *window, SDL_VideoDis
                            SubstructureNotifyMask | SubstructureRedirectMask, &e);
         }
 
+        if (!fullscreen) {
+            int dest_x = 0, dest_y = 0;
+            SDL_RelativeToGlobalForWindow(window,
+                                          window->windowed.x - data->border_left, window->windowed.y - data->border_top,
+                                          &dest_x, &dest_y);
+
+            /* Attempt to move the window */
+            X11_XMoveWindow(display, data->xwindow, dest_x, dest_y);
+        }
+
+
         /* Wait a brief time to see if the window manager decided to let this happen.
            If the window changes at all, even to an unexpected value, we break out. */
         X11_XSync(display, False);
@@ -1532,18 +1551,22 @@ static void X11_SetWindowFullscreenViaWM(_THIS, SDL_Window *window, SDL_VideoDis
                                       attrs.x, attrs.y, &x, &y, &childReturn);
 
             if (!caught_x11_error) {
-                SDL_bool window_changed = SDL_FALSE;
                 if ((x != orig_x) || (y != orig_y)) {
+                    orig_x = x;
+                    orig_y = y;
                     SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y);
-                    window_changed = SDL_TRUE;
+                    window_position_changed += 1;
                 }
 
                 if ((attrs.width != orig_w) || (attrs.height != orig_h)) {
+                    orig_w = attrs.width;
+                    orig_h = attrs.height;
                     SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED, attrs.width, attrs.height);
-                    window_changed = SDL_TRUE;
+                    window_size_changed = SDL_TRUE;
                 }
 
-                if (window_changed) {
+                /* Wait for at least 2 moves + 1 size changed to have valid values */
+                if (window_position_changed >= 2 && window_size_changed) {
                     break; /* window changed, time to go. */
                 }
             }