فهرست منبع

wayland: Use the compositor provided size during state transitions

Always use the size sent by the compositor when transitioning back to the floating state on the xdg-toplevel path, unless the client explicitly requested a new floating size while the window was in a fixed-size state.

If a window is requested to be maximized or made fullscreen while an uncommitted size request is pending, the surface will first be committed so that the compositor will set the new size when the window is restored.

Fixes the window being wrongly resized when leaving the maximized state in KDE.
Frank Praznik 9 ماه پیش
والد
کامیت
0d24b6e9f3
2فایلهای تغییر یافته به همراه59 افزوده شده و 25 حذف شده
  1. 57 25
      src/video/wayland/SDL_waylandwindow.c
  2. 2 0
      src/video/wayland/SDL_waylandwindow.h

+ 57 - 25
src/video/wayland/SDL_waylandwindow.c

@@ -484,9 +484,26 @@ static struct wl_callback_listener fullscreen_deadline_listener = {
     fullscreen_deadline_handler
 };
 
-static void FlushFullscreenEvents(SDL_Window *window)
+static void maximized_deadline_handler(void *data, struct wl_callback *callback, uint32_t callback_data)
 {
-    while (window->internal->fullscreen_deadline_count) {
+    /* Get the window from the ID as it may have been destroyed */
+    SDL_WindowID windowID = (SDL_WindowID)((uintptr_t)data);
+    SDL_Window *window = SDL_GetWindowFromID(windowID);
+
+    if (window && window->internal) {
+        window->internal->maximized_deadline_count--;
+    }
+
+    wl_callback_destroy(callback);
+}
+
+static struct wl_callback_listener maximized_deadline_listener = {
+    maximized_deadline_handler
+};
+
+static void FlushPendingEvents(SDL_Window *window)
+{
+    while (window->internal->fullscreen_deadline_count || window->internal->maximized_deadline_count) {
         WAYLAND_wl_display_roundtrip(window->internal->waylandData->display);
     }
 }
@@ -777,13 +794,14 @@ static void handle_configure_xdg_toplevel(void *data,
          * Ignore if less than or greater than max/min size.
          */
         if (window->flags & SDL_WINDOW_RESIZABLE) {
-            if ((floating && !wind->floating) ||
+            if ((floating && wind->pending_restored_size) ||
                 width == 0 || height == 0) {
-                /* This happens when we're being restored from a non-floating state,
-                 * or the compositor indicates that the size is up to the client, so
-                 * used the cached window size here.
+                /* This happens when we're being restored from a non-floating state
+                 * with a pending floating client size, or the compositor indicates
+                 * that the size is up to the client, so use the cached window size here.
                  */
                 if (floating) {
+                    wind->pending_restored_size = SDL_FALSE;
                     width = window->floating.w;
                     height = window->floating.h;
                 } else {
@@ -1158,27 +1176,22 @@ static void decoration_frame_configure(struct libdecor_frame *frame,
 
             OverrideLibdecorLimits(window);
         } else {
-            /*
-             * XXX: libdecor can send bogus content sizes that are +/- the height
-             *      of the title bar when hiding a window or transitioning from
-             *      non-floating to floating state, which distorts the window size.
-             *
-             *      Ignore any size values from libdecor in these scenarios in
-             *      favor of the cached window size.
+            /* XXX: The libdecor cairo plugin sends bogus content sizes that add the
+             *      height of the title bar when transitioning from a fixed-size to
+             *      floating state. Ignore the sent window dimensions in this case,
+             *      in favor of the cached value to avoid the window increasing in
+             *      size after every state transition.
              *
-             *      https://gitlab.gnome.org/jadahl/libdecor/-/issues/40
+             *      https://gitlab.freedesktop.org/libdecor/libdecor/-/issues/34
              */
-            const SDL_bool use_cached_size = !maximized && !tiled &&
-                                             ((floating && !wind->floating) ||
-                                              (window->is_hiding || (window->flags & SDL_WINDOW_HIDDEN)));
-
-            /* This will never set 0 for width/height unless the function returns false */
-            if (use_cached_size || !libdecor_configuration_get_content_size(configuration, frame, &width, &height)) {
+            if ((floating && (wind->pending_restored_size || (!wind->floating && !(window->flags & SDL_WINDOW_BORDERLESS)))) ||
+                !libdecor_configuration_get_content_size(configuration, frame, &width, &height)) {
                 /* This happens when we're being restored from a non-floating state,
                  * or the compositor indicates that the size is up to the client, so
                  * used the cached window size here.
                  */
                 if (floating) {
+                    wind->pending_restored_size = SDL_FALSE;
                     width = window->floating.w;
                     height = window->floating.h;
                 } else {
@@ -2114,7 +2127,7 @@ int Wayland_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window,
     }
 
     wind->drop_fullscreen_requests = SDL_TRUE;
-    FlushFullscreenEvents(window);
+    FlushPendingEvents(window);
     wind->drop_fullscreen_requests = SDL_FALSE;
 
     /* Nothing to do if the window is not fullscreen, and this isn't an explicit enter request. */
@@ -2223,6 +2236,7 @@ void Wayland_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, SDL_
 
 void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
 {
+    SDL_VideoData *viddata = _this->internal;
     SDL_WindowData *wind = window->internal;
 
     if (wind->show_hide_sync_required) {
@@ -2234,6 +2248,9 @@ void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
         if (!wind->shell_surface.libdecor.frame) {
             return; /* Can't do anything yet, wait for ShowWindow */
         }
+
+        /* Commit to preserve any pending size data. */
+        wl_surface_commit(wind->surface);
         libdecor_frame_set_maximized(wind->shell_surface.libdecor.frame);
     } else
 #endif
@@ -2241,8 +2258,15 @@ void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
         if (wind->shell_surface.xdg.roleobj.toplevel == NULL) {
             return; /* Can't do anything yet, wait for ShowWindow */
         }
+
+        /* Commit to preserve any pending size data. */
+        wl_surface_commit(wind->surface);
         xdg_toplevel_set_maximized(wind->shell_surface.xdg.roleobj.toplevel);
     }
+
+    ++wind->maximized_deadline_count;
+    struct wl_callback *cb = wl_display_sync(viddata->display);
+    wl_callback_add_listener(cb, &maximized_deadline_listener, (void *)((uintptr_t)window->id));
 }
 
 void Wayland_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
@@ -2531,7 +2555,7 @@ int Wayland_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window)
          * display via a set fullscreen call to make sure the window doesn't have a pending
          * leave fullscreen event that it might override.
          */
-        FlushFullscreenEvents(window);
+        FlushPendingEvents(window);
 
         /* XXX: Need to restore this after the roundtrip, as the requested coordinates might
          *      have been overwritten by the 'real' coordinates if a display enter/leave event
@@ -2565,12 +2589,13 @@ void Wayland_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
     SDL_WindowData *wind = window->internal;
 
     /* Fullscreen windows do not get explicitly resized, and not strictly
-     * obeying the size of maximized windows is a protocol violation.
+     * obeying the size of maximized windows is a protocol violation, so
+     * it is necessary to flush any of these pending state operations.
      *
      * Calling this on a custom surface is informative, so the size must
      * always be passed through.
      */
-    FlushFullscreenEvents(window);
+    FlushPendingEvents(window);
 
     if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) ||
         wind->shell_surface_type == WAYLAND_SURFACE_CUSTOM) {
@@ -2585,6 +2610,8 @@ void Wayland_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
         }
 
         ConfigureWindowGeometry(window);
+    } else {
+        wind->pending_restored_size = SDL_TRUE;
     }
 
     /* Always commit, as this may be in response to a min/max limit change. */
@@ -2641,7 +2668,12 @@ void Wayland_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window)
 
 int Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)
 {
-    WAYLAND_wl_display_roundtrip(_this->internal->display);
+    SDL_WindowData *wind = window->internal;
+
+    do {
+        WAYLAND_wl_display_roundtrip(_this->internal->display);
+    } while (wind->fullscreen_deadline_count || wind->maximized_deadline_count);
+
     return 0;
 }
 

+ 2 - 0
src/video/wayland/SDL_waylandwindow.h

@@ -157,6 +157,7 @@ struct SDL_WindowData
 
     SDL_DisplayID last_displayID;
     int fullscreen_deadline_count;
+    int maximized_deadline_count;
     Uint64 last_focus_event_time_ns;
     SDL_bool floating;
     SDL_bool suspended;
@@ -168,6 +169,7 @@ struct SDL_WindowData
     SDL_bool show_hide_sync_required;
     SDL_bool scale_to_display;
     SDL_bool modal_reparenting_required;
+    SDL_bool pending_restored_size;
     SDL_bool double_buffer;
 
     SDL_HitTestResult hit_test_result;