SDL: wayland: Make libdecor configure match xdg_toplevel

From 4562da622c830c87659af7cd8bd8da486289de84 Mon Sep 17 00:00:00 2001
From: Ethan Lee <[EMAIL REDACTED]>
Date: Mon, 2 Aug 2021 16:40:56 -0400
Subject: [PATCH] wayland: Make libdecor configure match xdg_toplevel

---
 src/video/wayland/SDL_waylandwindow.c | 91 +++++++++++++++++----------
 1 file changed, 57 insertions(+), 34 deletions(-)

diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c
index 5940f0b7c..dff4a0687 100644
--- a/src/video/wayland/SDL_waylandwindow.c
+++ b/src/video/wayland/SDL_waylandwindow.c
@@ -262,6 +262,7 @@ handle_configure_xdg_toplevel(void *data,
         wind->floating_height = height;
     }
 
+    /* Store this now so the xdg_surface configure knows what to resize to */
     window->w = width;
     window->h = height;
 }
@@ -284,69 +285,91 @@ decoration_frame_configure(struct libdecor_frame *frame,
                            struct libdecor_configuration *configuration,
                            void *user_data)
 {
-    SDL_WindowData *wind = user_data;
+    SDL_WindowData *wind = (SDL_WindowData *)user_data;
     SDL_Window *window = wind->sdlwindow;
-    int width, height;
-    enum libdecor_window_state window_state;
     struct libdecor_state *state;
 
-    /* window size */
-    if (!libdecor_configuration_get_content_size(configuration, frame, &width, &height)) {
-        width = window->w;
-        height = window->h;
-    }
-
-    Wayland_HandleResize(window, width, height, wind->scale_factor);
-    wind->shell_surface.libdecor.initial_configure_seen = SDL_TRUE;
+    enum libdecor_window_state window_state;
+    int width, height;
 
-    /* window state */
-    if (!libdecor_configuration_get_window_state(configuration, &window_state)) {
-        window_state = LIBDECOR_WINDOW_STATE_NONE;
+    SDL_bool focused = SDL_FALSE;
+    SDL_bool fullscreen = SDL_FALSE;
+    SDL_bool maximized = SDL_FALSE;
+    SDL_bool tiled = SDL_FALSE;
+    SDL_bool floating;
+
+    static const enum libdecor_window_state tiled_states = (
+        LIBDECOR_WINDOW_STATE_TILED_LEFT | LIBDECOR_WINDOW_STATE_TILED_RIGHT |
+        LIBDECOR_WINDOW_STATE_TILED_TOP | LIBDECOR_WINDOW_STATE_TILED_BOTTOM
+    );
+
+    /* Window State */
+    if (libdecor_configuration_get_window_state(configuration, &window_state)) {
+        fullscreen = (window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN) != 0;
+        maximized = (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) != 0;
+        focused = (window_state & LIBDECOR_WINDOW_STATE_ACTIVE) != 0;
+        tiled = (window_state & tiled_states) != 0;
     }
+    floating = !(fullscreen || maximized || tiled);
 
-   /* Always send maximized/restored/focus events; if the event is redundant it will
-     * automatically be discarded (see src/events/SDL_windowevents.c).
-     *
-     * No, we do not get minimize events from libdecor.
-     */
-    if (window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN) {
-        window->flags |= SDL_WINDOW_FULLSCREEN;
-    } else {
+    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);
-        } else {
+            fullscreen = SDL_TRUE;
+        }
+
+        /* Always send a maximized/restore event; if the event is redundant it will
+         * automatically be discarded (see src/events/SDL_windowevents.c)
+         *
+         * No, we do not get minimize events from libdecor.
+         */
+        if (!fullscreen) {
             SDL_SendWindowEvent(window,
-                                (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) ?
+                                maximized ?
                                     SDL_WINDOWEVENT_MAXIMIZED :
                                     SDL_WINDOWEVENT_RESTORED,
                                 0, 0);
         }
-        window->flags &= ~SDL_WINDOW_FULLSCREEN;
     }
 
+    /* Similar to maximized/restore events above, send focus events too! */
     SDL_SendWindowEvent(window,
-                        (window_state & LIBDECOR_WINDOW_STATE_ACTIVE) ?
+                        focused ?
                             SDL_WINDOWEVENT_FOCUS_GAINED :
                             SDL_WINDOWEVENT_FOCUS_LOST,
                         0, 0);
 
-    /* commit frame state */
-    state = libdecor_state_new(width, height);
-    libdecor_frame_commit(frame, state, configuration);
-    libdecor_state_free(state);
-
-    /* store floating dimensions */
-    if (libdecor_frame_is_floating(frame)) {
+    /* 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 {
+            width = window->w;
+            height = window->h;
+        }
+    } else if (floating) {
+        /* store current floating dimensions for restoring */
         wind->floating_width = width;
         wind->floating_height = height;
     }
 
+    /* Do the resize on the SDL side (this will set window->w/h)... */
+    Wayland_HandleResize(window, width, height, wind->scale_factor);
+    wind->shell_surface.libdecor.initial_configure_seen = SDL_TRUE;
+
+    /* ... then commit the changes on the libdecor side. */
+    state = libdecor_state_new(width, height);
+    libdecor_frame_commit(frame, state, configuration);
+    libdecor_state_free(state);
+
     /* Update the resize capability. Since this will change the capabilities and
      * commit a new frame state with the last known content dimension, this has
      * to be called after the new state has been commited and the new content
-     * dimensions were updated. */
+     * dimensions were updated.
+     */
     Wayland_SetWindowResizable(SDL_GetVideoDevice(), window,
                                window->flags & SDL_WINDOW_RESIZABLE);
 }