Commit 10979d37fbb0b789915be53c288dccf9e0c73a18

Ethan Lee 2021-08-05T12:42:03

wayland: Always assume configure is wrong for non-resizable windows. Configure events from compositors have an extremely annoying habit of giving us completely bogus sizes, from all sorts of places. Thankfully, the protocol gives us the ability to completely ignore the width/height and just stick with what we know, so for all windows that are not meant to be resized, pretend we never even got the width/height at all, the compositor is required to respect our dimensions whether they match configure's suggestion or not.

diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c
index eb31ebb..0003abe 100644
--- a/src/video/wayland/SDL_waylandwindow.c
+++ b/src/video/wayland/SDL_waylandwindow.c
@@ -183,6 +183,7 @@ handle_configure_xdg_toplevel(void *data,
 {
     SDL_WindowData *wind = (SDL_WindowData *)data;
     SDL_Window *window = wind->sdlwindow;
+    SDL_WaylandOutputData *driverdata;
 
     enum xdg_toplevel_state *state;
     SDL_bool fullscreen = SDL_FALSE;
@@ -209,15 +210,31 @@ handle_configure_xdg_toplevel(void *data,
         }
     }
 
+    driverdata = (SDL_WaylandOutputData *) SDL_GetDisplayForWindow(window)->driverdata;
+
     if (!fullscreen) {
         if (window->flags & SDL_WINDOW_FULLSCREEN) {
             /* We might need to re-enter fullscreen after being restored from minimized */
-            SDL_WaylandOutputData *driverdata = (SDL_WaylandOutputData *) SDL_GetDisplayForWindow(window)->driverdata;
             SetFullscreen(window, driverdata->output);
-            fullscreen = SDL_TRUE;
+
+            /* Foolishly do what the compositor says here. If it's wrong, don't
+             * blame us, we were explicitly instructed to do this.
+             */
+            window->w = width;
+            window->h = height;
+
+            /* This part is good though. */
+            if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
+                wind->scale_factor = driverdata->scale_factor;
+            }
+
+            return;
         }
 
         if (width == 0 || height == 0) {
+            /* This usually happens when we're being restored from a
+             * non-floating state, so use the cached floating size here.
+             */
             width = wind->floating_width;
             height = wind->floating_height;
         }
@@ -235,6 +252,12 @@ handle_configure_xdg_toplevel(void *data,
                 height = SDL_min(height, window->max_h);
             }
             height = SDL_max(height, window->min_h);
+        } else if (floating) {
+            /* If we're a fixed-size window, we know our size for sure.
+             * Always assume the configure is wrong.
+             */
+            width = window->windowed.w;
+            height = window->windowed.h;
         }
 
         /* Always send a maximized/restore event; if the event is redundant it will
@@ -242,28 +265,33 @@ handle_configure_xdg_toplevel(void *data,
          *
          * No, we do not get minimize events from xdg-shell.
          */
-        if (!fullscreen) {
-            SDL_SendWindowEvent(window,
-                                maximized ?
-                                    SDL_WINDOWEVENT_MAXIMIZED :
-                                    SDL_WINDOWEVENT_RESTORED,
-                                0, 0);
+        SDL_SendWindowEvent(window,
+                            maximized ?
+                                SDL_WINDOWEVENT_MAXIMIZED :
+                                SDL_WINDOWEVENT_RESTORED,
+                            0, 0);
+
+        /* Store current floating dimensions for restoring */
+        if (floating) {
+            wind->floating_width = width;
+            wind->floating_height = height;
         }
-    }
 
-    if (width == 0 || height == 0) {
-        return;
-    }
+        /* Store this now so the xdg_surface configure knows what to resize to */
+        window->w = width;
+        window->h = height;
+    } else {
+        /* For fullscreen, foolishly do what the compositor says. If it's wrong,
+         * don't blame us, we were explicitly instructed to do this.
+         */
+        window->w = width;
+        window->h = height;
 
-    if (floating) {
-        /* store current floating dimensions for restoring */
-        wind->floating_width = width;
-        wind->floating_height = height;
+        /* This part is good though. */
+        if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
+            wind->scale_factor = driverdata->scale_factor;
+        }
     }
-
-    /* Store this now so the xdg_surface configure knows what to resize to */
-    window->w = width;
-    window->h = height;
 }
 
 static void
@@ -286,6 +314,7 @@ decoration_frame_configure(struct libdecor_frame *frame,
 {
     SDL_WindowData *wind = (SDL_WindowData *)user_data;
     SDL_Window *window = wind->sdlwindow;
+    SDL_WaylandOutputData *driverdata;
     struct libdecor_state *state;
 
     enum libdecor_window_state window_state;
@@ -311,10 +340,11 @@ decoration_frame_configure(struct libdecor_frame *frame,
     }
     floating = !(fullscreen || maximized || tiled);
 
+    driverdata = (SDL_WaylandOutputData *) SDL_GetDisplayForWindow(window)->driverdata;
+
     if (!fullscreen) {
         if (window->flags & SDL_WINDOW_FULLSCREEN) {
             /* We might need to re-enter fullscreen after being restored from minimized */
-            SDL_WaylandOutputData *driverdata = (SDL_WaylandOutputData *) SDL_GetDisplayForWindow(window)->driverdata;
             SetFullscreen(window, driverdata->output);
             fullscreen = SDL_TRUE;
         }
@@ -340,17 +370,46 @@ decoration_frame_configure(struct libdecor_frame *frame,
                             SDL_WINDOWEVENT_FOCUS_LOST,
                         0, 0);
 
-    /* This will never set 0 for width/height unless the function returns false */
-    if (!libdecor_configuration_get_content_size(configuration, frame, &width, &height)) {
-        if (floating) {
-            width = wind->floating_width;
-            height = wind->floating_height;
-        } else {
+    /* For fullscreen or fixed-size windows we know our size.
+     * Always assume the configure is wrong.
+     */
+    if (fullscreen) {
+        /* FIXME: We have been explicitly told to respect the fullscreen size
+         * parameters here, even though they are known to be wrong on GNOME at
+         * bare minimum. If this is wrong, don't blame us, we were explicitly
+         * told to do this.
+         */
+        if (!libdecor_configuration_get_content_size(configuration, frame,
+                                                     &width, &height)) {
             width = window->w;
             height = window->h;
         }
-    } else if (floating) {
-        /* store current floating dimensions for restoring */
+
+        /* This part is good though. */
+        if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
+            wind->scale_factor = driverdata->scale_factor;
+        }
+    } else if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
+        width = window->windowed.w;
+        height = window->windowed.h;
+    } else {
+        /* This will never set 0 for width/height unless the function returns false */
+        if (!libdecor_configuration_get_content_size(configuration, frame, &width, &height)) {
+            if (floating) {
+                /* This usually happens when we're being restored from a
+                 * non-floating state, so use the cached floating size here.
+                 */
+                width = wind->floating_width;
+                height = wind->floating_height;
+            } else {
+                width = window->w;
+                height = window->h;
+            }
+        }
+    }
+
+    /* Store current floating dimensions for restoring */
+    if (floating) {
         wind->floating_width = width;
         wind->floating_height = height;
     }
@@ -948,23 +1007,6 @@ Wayland_SetWindowFullscreen(_THIS, SDL_Window * window,
     SetFullscreen(window, fullscreen ? output : NULL);
 
     WAYLAND_wl_display_flush(viddata->display);
-#ifdef HAVE_LIBDECOR_H
-    /* For libdecor we _have_ to dispatch immediately, because libdecor state
-     * strongly depends on the "current" state of the window. For example, if an
-     * application calls SetWindowSize right after this, libdecor will still
-     * think the window is fullscreen and not floating because configuration has
-     * not yet occurred, so the call will get completely ignored! So, take the
-     * time penalty and ensure that libdecor state matches SDL state.
-     *
-     * TODO: If Wayland_SetWindowSize ever stops checking for floating state,
-     * this can be removed.
-     *
-     * -flibit
-     */
-    if (viddata->shell.libdecor && (window->flags & SDL_WINDOW_SHOWN)) {
-        WAYLAND_wl_display_dispatch(viddata->display);
-    }
-#endif
 }
 
 void
@@ -1317,10 +1359,9 @@ void Wayland_SetWindowSize(_THIS, SDL_Window * window)
     }
 #endif
 
-    if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED))) {
-        wind->floating_width = window->w;
-        wind->floating_height = window->h;
-    }
+    /* windowed is unconditionally set, so we can trust it here */
+    wind->floating_width = window->windowed.w;
+    wind->floating_height = window->windowed.h;
 
     region = wl_compositor_create_region(data->compositor);
     wl_region_add(region, 0, 0, window->w, window->h);