[mutter] wayland/xdg-shell: Add toplevel bounds support



commit fadffe3fad1c92d9970c62837195c39c32636ce8
Author: Jonas Ã…dahl <jadahl gmail com>
Date:   Sat Dec 18 00:15:56 2021 +0100

    wayland/xdg-shell: Add toplevel bounds support
    
    This implements the new 'bounds' event that is part of the xdg_toplevel
    interface in the xdg-shell protocol. It aims to let clients create
    "good" default window sizes that depends on e.g. the resolution of the
    monitor the window will be mapped on, whether there are panels taking up
    space, and things like that.
    
    Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2167>

 src/backends/meta-monitor-manager-private.h        |   1 +
 src/tests/wayland-test-clients/meson.build         |   1 +
 .../wayland-test-clients/xdg-toplevel-bounds.c     | 358 +++++++++++++++++++++
 src/tests/wayland-unit-tests.c                     | 276 ++++++++++++++++
 src/wayland/meta-wayland-legacy-xdg-shell.c        |   2 +-
 src/wayland/meta-wayland-versions.h                |   2 +-
 src/wayland/meta-wayland-window-configuration.c    |  10 +-
 src/wayland/meta-wayland-window-configuration.h    |   8 +-
 src/wayland/meta-wayland-xdg-shell.c               |  24 +-
 src/wayland/meta-window-wayland.c                  |  20 ++
 10 files changed, 697 insertions(+), 5 deletions(-)
---
diff --git a/src/backends/meta-monitor-manager-private.h b/src/backends/meta-monitor-manager-private.h
index da85e55316..12bb840384 100644
--- a/src/backends/meta-monitor-manager-private.h
+++ b/src/backends/meta-monitor-manager-private.h
@@ -307,6 +307,7 @@ GList *             meta_monitor_manager_get_logical_monitors (MetaMonitorManage
 MetaLogicalMonitor *meta_monitor_manager_get_logical_monitor_from_number (MetaMonitorManager *manager,
                                                                           int                 number);
 
+META_EXPORT_TEST
 MetaLogicalMonitor *meta_monitor_manager_get_primary_logical_monitor (MetaMonitorManager *manager);
 
 MetaLogicalMonitor *meta_monitor_manager_get_logical_monitor_at (MetaMonitorManager *manager,
diff --git a/src/tests/wayland-test-clients/meson.build b/src/tests/wayland-test-clients/meson.build
index 2b3c83389e..48fad9afbe 100644
--- a/src/tests/wayland-test-clients/meson.build
+++ b/src/tests/wayland-test-clients/meson.build
@@ -53,6 +53,7 @@ wayland_test_clients = [
   'invalid-xdg-shell-actions',
   'xdg-apply-limits',
   'xdg-activation',
+  'xdg-toplevel-bounds',
 ]
 
 foreach test : wayland_test_clients
diff --git a/src/tests/wayland-test-clients/xdg-toplevel-bounds.c 
b/src/tests/wayland-test-clients/xdg-toplevel-bounds.c
new file mode 100644
index 0000000000..f0fc470581
--- /dev/null
+++ b/src/tests/wayland-test-clients/xdg-toplevel-bounds.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2021 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <wayland-client.h>
+
+#include "wayland-test-client-utils.h"
+
+#include "test-driver-client-protocol.h"
+#include "xdg-shell-client-protocol.h"
+
+typedef enum _State
+{
+  STATE_INIT = 0,
+  STATE_WAIT_FOR_CONFIGURE_1,
+  STATE_WAIT_FOR_FRAME_1,
+} State;
+
+static struct wl_display *display;
+static struct wl_registry *registry;
+static struct wl_compositor *compositor;
+static struct xdg_wm_base *xdg_wm_base;
+static struct wl_shm *shm;
+static struct test_driver *test_driver;
+
+static struct wl_surface *surface;
+static struct xdg_surface *xdg_surface;
+static struct xdg_toplevel *xdg_toplevel;
+
+static struct wl_callback *frame_callback;
+
+static gboolean running;
+
+static State state;
+static int32_t pending_bounds_width;
+static int32_t pending_bounds_height;
+
+static void
+init_surface (void)
+{
+  xdg_toplevel_set_title (xdg_toplevel, "toplevel-bounds-test");
+  wl_surface_commit (surface);
+}
+
+static void
+handle_buffer_release (void             *data,
+                       struct wl_buffer *buffer)
+{
+  wl_buffer_destroy (buffer);
+}
+
+static const struct wl_buffer_listener buffer_listener = {
+  handle_buffer_release
+};
+
+static gboolean
+create_shm_buffer (int                width,
+                   int                height,
+                   struct wl_buffer **out_buffer,
+                   void             **out_data,
+                   int               *out_size)
+{
+  struct wl_shm_pool *pool;
+  static struct wl_buffer *buffer;
+  int fd, size, stride;
+  int bytes_per_pixel;
+  void *data;
+
+  bytes_per_pixel = 4;
+  stride = width * bytes_per_pixel;
+  size = stride * height;
+
+  fd = create_anonymous_file (size);
+  if (fd < 0)
+    {
+      fprintf (stderr, "Creating a buffer file for %d B failed: %m\n",
+               size);
+      return FALSE;
+    }
+
+  data = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+  if (data == MAP_FAILED)
+    {
+      fprintf (stderr, "mmap failed: %m\n");
+      close (fd);
+      return FALSE;
+    }
+
+  pool = wl_shm_create_pool (shm, fd, size);
+  buffer = wl_shm_pool_create_buffer (pool, 0,
+                                      width, height,
+                                      stride,
+                                      WL_SHM_FORMAT_ARGB8888);
+  wl_buffer_add_listener (buffer, &buffer_listener, buffer);
+  wl_shm_pool_destroy (pool);
+  close (fd);
+
+  *out_buffer = buffer;
+  *out_data = data;
+  *out_size = size;
+
+  return TRUE;
+}
+
+static void
+fill (void    *buffer_data,
+      int      width,
+      int      height,
+      uint32_t color)
+{
+  uint32_t *pixels = buffer_data;
+  int x, y;
+
+  for (y = 0; y < height; y++)
+    {
+      for (x = 0; x < width; x++)
+        pixels[y * width + x] = color;
+    }
+}
+
+static void
+draw (struct wl_surface *surface,
+      int                width,
+      int                height,
+      uint32_t           color)
+{
+  struct wl_buffer *buffer;
+  void *buffer_data;
+  int size;
+
+  if (!create_shm_buffer (width, height,
+                          &buffer, &buffer_data, &size))
+    g_error ("Failed to create shm buffer");
+
+  fill (buffer_data, width, height, color);
+
+  wl_surface_attach (surface, buffer, 0, 0);
+}
+
+static void
+draw_main (int width,
+           int height)
+{
+  draw (surface, width, height, 0xff00ff00);
+}
+
+static void
+handle_xdg_toplevel_configure (void                *data,
+                               struct xdg_toplevel *xdg_toplevel,
+                               int32_t              width,
+                               int32_t              height,
+                               struct wl_array     *state)
+{
+}
+
+static void
+handle_xdg_toplevel_close(void                *data,
+                          struct xdg_toplevel *xdg_toplevel)
+{
+  g_assert_not_reached ();
+}
+
+static void
+handle_xdg_toplevel_configure_bounds (void                *data,
+                                      struct xdg_toplevel *xdg_toplevel,
+                                      int32_t              bounds_width,
+                                      int32_t              bounds_height)
+{
+  pending_bounds_width = bounds_width;
+  pending_bounds_height = bounds_height;
+}
+
+static const struct xdg_toplevel_listener xdg_toplevel_listener = {
+  handle_xdg_toplevel_configure,
+  handle_xdg_toplevel_close,
+  handle_xdg_toplevel_configure_bounds,
+};
+
+static void
+handle_frame_callback (void               *data,
+                       struct wl_callback *callback,
+                       uint32_t            time)
+{
+  switch (state)
+    {
+    case STATE_WAIT_FOR_FRAME_1:
+      test_driver_sync_point (test_driver, 1, NULL);
+      break;
+    case STATE_INIT:
+    case STATE_WAIT_FOR_CONFIGURE_1:
+      g_assert_not_reached ();
+    }
+}
+
+static const struct wl_callback_listener frame_listener = {
+  handle_frame_callback,
+};
+
+static void
+handle_xdg_surface_configure (void               *data,
+                              struct xdg_surface *xdg_surface,
+                              uint32_t            serial)
+{
+  switch (state)
+    {
+    case STATE_INIT:
+      g_assert_not_reached ();
+    case STATE_WAIT_FOR_CONFIGURE_1:
+      g_assert (pending_bounds_width > 0);
+      g_assert (pending_bounds_height > 0);
+
+      draw_main (pending_bounds_width - 10,
+                 pending_bounds_height - 10);
+      state = STATE_WAIT_FOR_FRAME_1;
+      break;
+    case STATE_WAIT_FOR_FRAME_1:
+      break;
+    }
+
+  xdg_surface_ack_configure (xdg_surface, serial);
+  frame_callback = wl_surface_frame (surface);
+  wl_callback_add_listener (frame_callback, &frame_listener, NULL);
+  wl_surface_commit (surface);
+  wl_display_flush (display);
+}
+
+static const struct xdg_surface_listener xdg_surface_listener = {
+  handle_xdg_surface_configure,
+};
+
+static void
+handle_xdg_wm_base_ping (void               *data,
+                         struct xdg_wm_base *xdg_wm_base,
+                         uint32_t            serial)
+{
+  xdg_wm_base_pong (xdg_wm_base, serial);
+}
+
+static const struct xdg_wm_base_listener xdg_wm_base_listener = {
+  handle_xdg_wm_base_ping,
+};
+
+static void
+test_driver_handle_sync_event (void               *data,
+                               struct test_driver *test_driver,
+                               uint32_t            serial)
+{
+  g_assert (serial == 0);
+
+  exit (EXIT_SUCCESS);
+}
+
+static const struct test_driver_listener test_driver_listener = {
+  test_driver_handle_sync_event,
+};
+
+static void
+handle_registry_global (void               *data,
+                        struct wl_registry *registry,
+                        uint32_t            id,
+                        const char         *interface,
+                        uint32_t            version)
+{
+  if (strcmp (interface, "wl_compositor") == 0)
+    {
+      compositor = wl_registry_bind (registry, id, &wl_compositor_interface, 1);
+    }
+  else if (strcmp (interface, "xdg_wm_base") == 0)
+    {
+      xdg_wm_base = wl_registry_bind (registry, id,
+                                      &xdg_wm_base_interface, 4);
+      xdg_wm_base_add_listener (xdg_wm_base, &xdg_wm_base_listener, NULL);
+    }
+  else if (strcmp (interface, "wl_shm") == 0)
+    {
+      shm = wl_registry_bind (registry,
+                              id, &wl_shm_interface, 1);
+    }
+  else if (strcmp (interface, "test_driver") == 0)
+    {
+      test_driver = wl_registry_bind (registry, id, &test_driver_interface, 1);
+      test_driver_add_listener (test_driver, &test_driver_listener, NULL);
+    }
+}
+
+static void
+handle_registry_global_remove (void               *data,
+                               struct wl_registry *registry,
+                               uint32_t            name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+  handle_registry_global,
+  handle_registry_global_remove
+};
+
+int
+main (int    argc,
+      char **argv)
+{
+  display = wl_display_connect (NULL);
+  registry = wl_display_get_registry (display);
+  wl_registry_add_listener (registry, &registry_listener, NULL);
+  wl_display_roundtrip (display);
+
+  if (!shm)
+    {
+      fprintf (stderr, "No wl_shm global\n");
+      return EXIT_FAILURE;
+    }
+
+  if (!xdg_wm_base)
+    {
+      fprintf (stderr, "No xdg_wm_base global\n");
+      return EXIT_FAILURE;
+    }
+
+  wl_display_roundtrip (display);
+
+  surface = wl_compositor_create_surface (compositor);
+  xdg_surface = xdg_wm_base_get_xdg_surface (xdg_wm_base, surface);
+  xdg_surface_add_listener (xdg_surface, &xdg_surface_listener, NULL);
+  xdg_toplevel = xdg_surface_get_toplevel (xdg_surface);
+  xdg_toplevel_add_listener (xdg_toplevel, &xdg_toplevel_listener, NULL);
+
+  init_surface ();
+  state = STATE_WAIT_FOR_CONFIGURE_1;
+
+  wl_surface_commit (surface);
+
+  running = TRUE;
+  while (running)
+    {
+      if (wl_display_dispatch (display) == -1)
+        return EXIT_FAILURE;
+    }
+
+  return EXIT_SUCCESS;
+}
diff --git a/src/tests/wayland-unit-tests.c b/src/tests/wayland-unit-tests.c
index 4e95ab4f52..9c42b16332 100644
--- a/src/tests/wayland-unit-tests.c
+++ b/src/tests/wayland-unit-tests.c
@@ -25,6 +25,8 @@
 #include "core/window-private.h"
 #include "meta-test/meta-context-test.h"
 #include "tests/meta-test-utils.h"
+#include "meta/meta-later.h"
+#include "meta/meta-workspace-manager.h"
 #include "tests/meta-wayland-test-driver.h"
 #include "tests/meta-wayland-test-utils.h"
 #include "wayland/meta-wayland-surface.h"
@@ -363,6 +365,276 @@ toplevel_activation (void)
   meta_wayland_test_client_finish (data.wayland_test_client);
 }
 
+static void
+on_sync_point (MetaWaylandTestDriver *test_driver,
+               unsigned int           sequence,
+               struct wl_resource    *surface_resource,
+               struct wl_client      *wl_client,
+               unsigned int          *latest_sequence)
+{
+  *latest_sequence = sequence;
+}
+
+static void
+wait_for_sync_point (unsigned int sync_point)
+{
+  gulong handler_id;
+  unsigned int latest_sequence = 0;
+
+  handler_id = g_signal_connect (test_driver, "sync-point",
+                                 G_CALLBACK (on_sync_point),
+                                 &latest_sequence);
+  while (latest_sequence != sync_point)
+    g_main_context_iteration (NULL, TRUE);
+  g_signal_handler_disconnect (test_driver, handler_id);
+}
+
+static gboolean
+mark_later_as_done (gpointer user_data)
+{
+  gboolean *done = user_data;
+
+  *done = TRUE;
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+wait_until_after_paint (void)
+{
+  MetaDisplay *display = meta_context_get_display (test_context);
+  MetaCompositor *compositor = meta_display_get_compositor (display);
+  MetaLaters *laters = meta_compositor_get_laters (compositor);
+  gboolean done;
+
+  done = FALSE;
+  meta_laters_add (laters,
+                   META_LATER_BEFORE_REDRAW,
+                   mark_later_as_done,
+                   &done,
+                   NULL);
+  while (!done)
+    g_main_context_iteration (NULL, FALSE);
+
+  done = FALSE;
+  meta_laters_add (laters,
+                   META_LATER_IDLE,
+                   mark_later_as_done,
+                   &done,
+                   NULL);
+  while (!done)
+    g_main_context_iteration (NULL, FALSE);
+}
+
+static void
+set_struts (MetaRectangle rect,
+            MetaSide      side)
+{
+  MetaDisplay *display = meta_context_get_display (test_context);
+  MetaWorkspaceManager *workspace_manager =
+    meta_display_get_workspace_manager (display);
+  GList *workspaces =
+    meta_workspace_manager_get_workspaces (workspace_manager);
+  MetaStrut strut;
+  g_autoptr (GSList) struts = NULL;
+  GList *l;
+
+  strut = (MetaStrut) { .rect = rect, .side = side };
+  struts = g_slist_append (NULL, &strut);
+
+  for (l = workspaces; l; l = l->next)
+    {
+      MetaWorkspace *workspace = l->data;
+
+      meta_workspace_set_builtin_struts (workspace, struts);
+    }
+}
+
+static void
+clear_struts (void)
+{
+  MetaDisplay *display = meta_context_get_display (test_context);
+  MetaWorkspaceManager *workspace_manager =
+    meta_display_get_workspace_manager (display);
+  GList *workspaces =
+    meta_workspace_manager_get_workspaces (workspace_manager);
+  GList *l;
+
+  for (l = workspaces; l; l = l->next)
+    {
+      MetaWorkspace *workspace = l->data;
+
+      meta_workspace_set_builtin_struts (workspace, NULL);
+    }
+}
+
+static MetaRectangle
+get_primary_logical_monitor_layout (void)
+{
+  MetaBackend *backend = meta_context_get_backend (test_context);
+  MetaMonitorManager *monitor_manager =
+    meta_backend_get_monitor_manager (backend);
+  MetaLogicalMonitor *logical_monitor;
+
+  logical_monitor =
+    meta_monitor_manager_get_primary_logical_monitor (monitor_manager);
+  return meta_logical_monitor_get_layout (logical_monitor);
+}
+
+static void
+toplevel_bounds_struts (void)
+{
+  MetaWaylandTestClient *wayland_test_client;
+  MetaWindow *window;
+  MetaRectangle logical_monitor_layout;
+  MetaRectangle work_area;
+
+  /*
+   * This test case makes sure that setting and changing struts result in the
+   * right bounds are sent.
+   */
+
+  logical_monitor_layout = get_primary_logical_monitor_layout ();
+  set_struts ((MetaRectangle) {
+                .x = 0,
+                .y = 0,
+                .width = logical_monitor_layout.width,
+                .height = 10,
+              },
+              META_SIDE_TOP);
+
+  wayland_test_client = meta_wayland_test_client_new ("xdg-toplevel-bounds");
+
+  wait_for_sync_point (1);
+  wait_until_after_paint ();
+
+  window = find_client_window ("toplevel-bounds-test");
+
+  g_assert_nonnull (window->monitor);
+  meta_window_get_work_area_current_monitor (window, &work_area);
+  g_assert_cmpint (work_area.width, ==, logical_monitor_layout.width);
+  g_assert_cmpint (work_area.height, ==, logical_monitor_layout.height - 10);
+
+  g_assert_cmpint (window->rect.width, ==, work_area.width - 10);
+  g_assert_cmpint (window->rect.height, ==, work_area.height - 10);
+
+  meta_wayland_test_driver_emit_sync_event (test_driver, 0);
+  meta_wayland_test_client_finish (wayland_test_client);
+
+  clear_struts ();
+
+  wayland_test_client = meta_wayland_test_client_new ("xdg-toplevel-bounds");
+
+  wait_for_sync_point (1);
+  wait_until_after_paint ();
+
+  window = find_client_window ("toplevel-bounds-test");
+  g_assert_nonnull (window->monitor);
+  meta_window_get_work_area_current_monitor (window, &work_area);
+  g_assert_cmpint (work_area.width, ==, logical_monitor_layout.width);
+  g_assert_cmpint (work_area.height, ==, logical_monitor_layout.height);
+
+  g_assert_cmpint (window->rect.width, ==, work_area.width - 10);
+  g_assert_cmpint (window->rect.height, ==, work_area.height - 10);
+
+  meta_wayland_test_driver_emit_sync_event (test_driver, 0);
+  meta_wayland_test_client_finish (wayland_test_client);
+}
+
+static void
+wait_for_cursor_position (float x,
+                          float y)
+{
+  MetaBackend *backend = meta_context_get_backend (test_context);
+  MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
+  graphene_point_t point;
+
+  while (TRUE)
+    {
+      meta_cursor_tracker_get_pointer (cursor_tracker, &point, NULL);
+      if (G_APPROX_VALUE (x, point.x, FLT_EPSILON) &&
+          G_APPROX_VALUE (y, point.y, FLT_EPSILON))
+        break;
+
+      g_main_context_iteration (NULL, TRUE);
+    }
+}
+
+static void
+toplevel_bounds_monitors (void)
+{
+  MetaBackend *backend = meta_context_get_backend (test_context);
+  ClutterSeat *seat;
+  g_autoptr (MetaVirtualMonitor) second_virtual_monitor = NULL;
+  MetaWaylandTestClient *wayland_test_client;
+  MetaRectangle logical_monitor_layout;
+  MetaRectangle work_area;
+  MetaWindow *window;
+
+  /*
+   * This test case creates two monitors, with different sizes, with a fake
+   * panel on top of the primary monitor. It then makes sure launching on both
+   * monitors results in the correct bounds.
+   */
+
+  seat = meta_backend_get_default_seat (backend);
+  virtual_pointer = clutter_seat_create_virtual_device (seat,
+                                                        CLUTTER_POINTER_DEVICE);
+
+  second_virtual_monitor = meta_create_test_monitor (test_context,
+                                                     300, 200, 60.0);
+
+  logical_monitor_layout = get_primary_logical_monitor_layout ();
+  set_struts ((MetaRectangle) {
+                .x = 0,
+                .y = 0,
+                .width = logical_monitor_layout.width,
+                .height = 10,
+              },
+              META_SIDE_TOP);
+
+  wayland_test_client = meta_wayland_test_client_new ("xdg-toplevel-bounds");
+
+  wait_for_sync_point (1);
+  wait_until_after_paint ();
+
+  window = find_client_window ("toplevel-bounds-test");
+
+  g_assert_nonnull (window->monitor);
+  meta_window_get_work_area_current_monitor (window, &work_area);
+  g_assert_cmpint (work_area.width, ==, logical_monitor_layout.width);
+  g_assert_cmpint (work_area.height, ==, logical_monitor_layout.height - 10);
+
+  g_assert_cmpint (window->rect.width, ==, work_area.width - 10);
+  g_assert_cmpint (window->rect.height, ==, work_area.height - 10);
+
+  meta_wayland_test_driver_emit_sync_event (test_driver, 0);
+  meta_wayland_test_client_finish (wayland_test_client);
+
+  clutter_virtual_input_device_notify_absolute_motion (virtual_pointer,
+                                                       CLUTTER_CURRENT_TIME,
+                                                       550.0, 100.0);
+  wait_for_cursor_position (550.0, 100.0);
+
+  wayland_test_client = meta_wayland_test_client_new ("xdg-toplevel-bounds");
+
+  wait_for_sync_point (1);
+  wait_until_after_paint ();
+
+  window = find_client_window ("toplevel-bounds-test");
+
+  g_assert_nonnull (window->monitor);
+  meta_window_get_work_area_current_monitor (window, &work_area);
+  g_assert_cmpint (work_area.width, ==, 300);
+  g_assert_cmpint (work_area.height, ==, 200);
+
+  g_assert_cmpint (window->rect.width, ==, 300 - 10);
+  g_assert_cmpint (window->rect.height, ==, 200 - 10);
+
+  meta_wayland_test_driver_emit_sync_event (test_driver, 0);
+  meta_wayland_test_client_finish (wayland_test_client);
+}
+
 static void
 on_before_tests (void)
 {
@@ -398,6 +670,10 @@ init_tests (void)
                    toplevel_apply_limits);
   g_test_add_func ("/wayland/toplevel/activation",
                    toplevel_activation);
+  g_test_add_func ("/wayland/toplevel/bounds/struts",
+                   toplevel_bounds_struts);
+  g_test_add_func ("/wayland/toplevel/bounds/monitors",
+                   toplevel_bounds_monitors);
 }
 
 int
diff --git a/src/wayland/meta-wayland-legacy-xdg-shell.c b/src/wayland/meta-wayland-legacy-xdg-shell.c
index cf822bcb69..b34a836c58 100644
--- a/src/wayland/meta-wayland-legacy-xdg-shell.c
+++ b/src/wayland/meta-wayland-legacy-xdg-shell.c
@@ -685,7 +685,7 @@ meta_wayland_zxdg_toplevel_v6_apply_state (MetaWaylandSurfaceRole  *surface_role
     {
       MetaWaylandWindowConfiguration *configuration;
 
-      configuration = meta_wayland_window_configuration_new_empty ();
+      configuration = meta_wayland_window_configuration_new_empty (0, 0);
       meta_wayland_zxdg_toplevel_v6_send_configure (xdg_toplevel,
                                                     configuration);
       meta_wayland_window_configuration_free (configuration);
diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h
index 598bdd2e57..cd1a73ea63 100644
--- a/src/wayland/meta-wayland-versions.h
+++ b/src/wayland/meta-wayland-versions.h
@@ -37,7 +37,7 @@
 /* Global/master objects (version exported by wl_registry and negotiated through bind) */
 #define META_WL_COMPOSITOR_VERSION          4
 #define META_WL_DATA_DEVICE_MANAGER_VERSION 3
-#define META_XDG_WM_BASE_VERSION            3
+#define META_XDG_WM_BASE_VERSION            4
 #define META_ZXDG_SHELL_V6_VERSION          1
 #define META_WL_SEAT_VERSION                5
 #define META_WL_OUTPUT_VERSION              2
diff --git a/src/wayland/meta-wayland-window-configuration.c b/src/wayland/meta-wayland-window-configuration.c
index c1712136fb..702c95a620 100644
--- a/src/wayland/meta-wayland-window-configuration.c
+++ b/src/wayland/meta-wayland-window-configuration.c
@@ -43,6 +43,8 @@ is_window_size_fixed (MetaWindow *window)
 MetaWaylandWindowConfiguration *
 meta_wayland_window_configuration_new (MetaWindow          *window,
                                        MetaRectangle        rect,
+                                       int                  bounds_width,
+                                       int                  bounds_height,
                                        int                  scale,
                                        MetaMoveResizeFlags  flags,
                                        MetaGravity          gravity)
@@ -53,6 +55,9 @@ meta_wayland_window_configuration_new (MetaWindow          *window,
   *configuration = (MetaWaylandWindowConfiguration) {
     .serial = ++global_serial_counter,
 
+    .bounds_width = bounds_width,
+    .bounds_height = bounds_height,
+
     .scale = scale,
     .gravity = gravity,
     .flags = flags,
@@ -108,7 +113,8 @@ meta_wayland_window_configuration_new_relative (int rel_x,
 }
 
 MetaWaylandWindowConfiguration *
-meta_wayland_window_configuration_new_empty (void)
+meta_wayland_window_configuration_new_empty (int bounds_width,
+                                             int bounds_height)
 {
   MetaWaylandWindowConfiguration *configuration;
 
@@ -116,6 +122,8 @@ meta_wayland_window_configuration_new_empty (void)
   *configuration = (MetaWaylandWindowConfiguration) {
     .serial = ++global_serial_counter,
     .scale = 1,
+    .bounds_width = bounds_width,
+    .bounds_height = bounds_height,
   };
 
   return configuration;
diff --git a/src/wayland/meta-wayland-window-configuration.h b/src/wayland/meta-wayland-window-configuration.h
index 7e91a778e9..c4c0f2e55e 100644
--- a/src/wayland/meta-wayland-window-configuration.h
+++ b/src/wayland/meta-wayland-window-configuration.h
@@ -46,10 +46,15 @@ struct _MetaWaylandWindowConfiguration
   int scale;
   MetaGravity gravity;
   MetaMoveResizeFlags flags;
+
+  int bounds_width;
+  int bounds_height;
 };
 
 MetaWaylandWindowConfiguration * meta_wayland_window_configuration_new (MetaWindow          *window,
                                                                         MetaRectangle        rect,
+                                                                        int                  max_width,
+                                                                        int                  max_height,
                                                                         int                  scale,
                                                                         MetaMoveResizeFlags  flags,
                                                                         MetaGravity          gravity);
@@ -60,7 +65,8 @@ MetaWaylandWindowConfiguration * meta_wayland_window_configuration_new_relative
                                                                                  int height,
                                                                                  int scale);
 
-MetaWaylandWindowConfiguration * meta_wayland_window_configuration_new_empty (void);
+MetaWaylandWindowConfiguration * meta_wayland_window_configuration_new_empty (int bounds_width,
+                                                                              int bounds_height);
 
 void meta_wayland_window_configuration_free (MetaWaylandWindowConfiguration *configuration);
 
diff --git a/src/wayland/meta-wayland-xdg-shell.c b/src/wayland/meta-wayland-xdg-shell.c
index acd0913d11..59a0f443ec 100644
--- a/src/wayland/meta-wayland-xdg-shell.c
+++ b/src/wayland/meta-wayland-xdg-shell.c
@@ -700,6 +700,18 @@ meta_wayland_xdg_toplevel_send_configure (MetaWaylandXdgToplevel         *xdg_to
   wl_array_init (&states);
   fill_states (xdg_toplevel, &states);
 
+  if (wl_resource_get_version (xdg_toplevel->resource) >=
+      XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION &&
+      configuration->bounds_width > 0 &&
+      configuration->bounds_height > 0)
+    {
+      xdg_toplevel_send_configure_bounds (xdg_toplevel->resource,
+                                          (configuration->bounds_width /
+                                           configuration->scale),
+                                          (configuration->bounds_height /
+                                           configuration->scale));
+    }
+
   xdg_toplevel_send_configure (xdg_toplevel->resource,
                                configuration->width / configuration->scale,
                                configuration->height / configuration->scale,
@@ -777,8 +789,18 @@ meta_wayland_xdg_toplevel_apply_state (MetaWaylandSurfaceRole  *surface_role,
   if (!xdg_surface_priv->configure_sent)
     {
       MetaWaylandWindowConfiguration *configuration;
+      int bounds_width;
+      int bounds_height;
+
+      if (!meta_window_calculate_bounds (window, &bounds_width, &bounds_height))
+        {
+          bounds_width = 0;
+          bounds_height = 0;
+        }
 
-      configuration = meta_wayland_window_configuration_new_empty ();
+      configuration =
+        meta_wayland_window_configuration_new_empty (bounds_width,
+                                                     bounds_height);
       meta_wayland_xdg_toplevel_send_configure (xdg_toplevel, configuration);
       meta_wayland_window_configuration_free (configuration);
       return;
diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c
index 2a923ef8fa..d0fbde8438 100644
--- a/src/wayland/meta-window-wayland.c
+++ b/src/wayland/meta-window-wayland.c
@@ -181,6 +181,8 @@ surface_state_changed (MetaWindow *window)
 {
   MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window);
   MetaWaylandWindowConfiguration *configuration;
+  int bounds_width;
+  int bounds_height;
 
   /* don't send notify when the window is being unmanaged */
   if (window->unmanaging)
@@ -188,9 +190,16 @@ surface_state_changed (MetaWindow *window)
 
   g_return_if_fail (wl_window->has_last_sent_configuration);
 
+  if (!meta_window_calculate_bounds (window, &bounds_width, &bounds_height))
+    {
+      bounds_width = 0;
+      bounds_height = 0;
+    }
+
   configuration =
     meta_wayland_window_configuration_new (window,
                                            wl_window->last_sent_rect,
+                                           bounds_width, bounds_height,
                                            wl_window->last_sent_geometry_scale,
                                            META_MOVE_RESIZE_STATE_CHANGED,
                                            wl_window->last_sent_gravity);
@@ -345,6 +354,8 @@ meta_window_wayland_move_resize_internal (MetaWindow                *window,
                flags & META_MOVE_RESIZE_STATE_CHANGED)
         {
           MetaWaylandWindowConfiguration *configuration;
+          int bounds_width;
+          int bounds_height;
 
           if (!meta_wayland_surface_get_buffer (window->surface) &&
               !META_WINDOW_MAXIMIZED (window) &&
@@ -352,9 +363,18 @@ meta_window_wayland_move_resize_internal (MetaWindow                *window,
               !meta_window_is_fullscreen (window))
             return;
 
+          if (!meta_window_calculate_bounds (window,
+                                             &bounds_width,
+                                             &bounds_height))
+            {
+              bounds_width = 0;
+              bounds_height = 0;
+            }
+
           configuration =
             meta_wayland_window_configuration_new (window,
                                                    configured_rect,
+                                                   bounds_width, bounds_height,
                                                    geometry_scale,
                                                    flags,
                                                    gravity);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]