From 4c1fabde5e15f8f49bed2ce716f700377c7ab727 Mon Sep 17 00:00:00 2001 From: Sunderland93 Date: Wed, 13 Aug 2025 12:26:37 +0400 Subject: [PATCH] [Wayland] Add support for xdg_toplevel.configure_bounds() This PR adds support for xdg_toplevel_configure_bounds(), which is aims to communicate the maximum size a surface should be created with, and loosely corresponds to the concept of "work area" in the X11 world. It was introduced in v4 of xdg-shell protocol, so also bump it's version. Adapted from Mutter: https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2167 --- meson.build | 2 +- src/core/window-private.h | 4 + src/core/window.c | 33 ++++- src/wayland/meta-wayland-legacy-xdg-shell.c | 2 +- src/wayland/meta-wayland-versions.h | 2 +- .../meta-wayland-window-configuration.c | 63 ++++++--- .../meta-wayland-window-configuration.h | 14 +- src/wayland/meta-wayland-xdg-shell.c | 24 +++- src/wayland/meta-window-wayland.c | 122 ++++++++---------- 9 files changed, 171 insertions(+), 95 deletions(-) diff --git a/meson.build b/meson.build index 13e77d05e..fa91dc3a8 100644 --- a/meson.build +++ b/meson.build @@ -38,7 +38,7 @@ gudev_req = '>= 232' # wayland version requirements wayland_server_req = '>= 1.13.0' -wayland_protocols_req = '>= 1.19' +wayland_protocols_req = '>= 1.25' # native backend version requirements libinput_req = '>= 1.7' diff --git a/src/core/window-private.h b/src/core/window-private.h index c3bd117aa..7fb122974 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -908,4 +908,8 @@ gboolean meta_window_shortcuts_inhibited (MetaWindow *window, ClutterInputDevice *source); gboolean meta_window_is_stackable (MetaWindow *window); gboolean meta_window_is_focus_async (MetaWindow *window); + +gboolean meta_window_calculate_bounds (MetaWindow *window, + int *bounds_width, + int *bounds_height); #endif diff --git a/src/core/window.c b/src/core/window.c index 4395676f5..1cae3f713 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -1047,6 +1047,7 @@ _meta_window_shared_new (MetaDisplay *display, MetaCompEffect effect, XWindowAttributes *attrs) { + MetaBackend *backend = meta_get_backend (); MetaWorkspaceManager *workspace_manager = display->workspace_manager; MetaWindow *window; @@ -1241,7 +1242,11 @@ _meta_window_shared_new (MetaDisplay *display, window->compositor_private = NULL; - window->monitor = meta_window_calculate_main_logical_monitor (window); + if (window->rect.width > 0 && window->rect.height > 0) + window->monitor = meta_window_calculate_main_logical_monitor (window); + else + window->monitor = meta_backend_get_current_logical_monitor (backend); + if (window->monitor) window->preferred_output_winsys_id = window->monitor->winsys_id; else @@ -9439,3 +9444,29 @@ meta_window_get_icon_name (MetaWindow *window) return window->theme_icon_name; } + +gboolean +meta_window_calculate_bounds (MetaWindow *window, + int *bounds_width, + int *bounds_height) +{ + MetaLogicalMonitor *main_monitor; + + main_monitor = meta_window_get_main_logical_monitor (window); + if (main_monitor) + { + MetaRectangle work_area; + + meta_window_get_work_area_for_logical_monitor (window, + main_monitor, + &work_area); + + *bounds_width = work_area.width; + *bounds_height = work_area.height; + return TRUE; + } + else + { + return FALSE; + } +} diff --git a/src/wayland/meta-wayland-legacy-xdg-shell.c b/src/wayland/meta-wayland-legacy-xdg-shell.c index 90eacab36..e06bdb53b 100644 --- a/src/wayland/meta-wayland-legacy-xdg-shell.c +++ b/src/wayland/meta-wayland-legacy-xdg-shell.c @@ -680,7 +680,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 9898d5be1..7e2aff0f4 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_SHELL_VERSION 1 #define META_WL_SEAT_VERSION 5 diff --git a/src/wayland/meta-wayland-window-configuration.c b/src/wayland/meta-wayland-window-configuration.c index a0ee8f805..2e97d3b2c 100644 --- a/src/wayland/meta-wayland-window-configuration.c +++ b/src/wayland/meta-wayland-window-configuration.c @@ -24,14 +24,30 @@ static uint32_t global_serial_counter = 0; +static gboolean +is_window_size_fixed (MetaWindow *window) +{ + if (meta_window_is_fullscreen (window)) + return TRUE; + + if (meta_window_get_maximized (window) | + (META_MAXIMIZE_VERTICAL | META_MAXIMIZE_VERTICAL)) + return TRUE; + + if (meta_window_get_tile_mode (window) != META_TILE_NONE) + return TRUE; + + return FALSE; +} + MetaWaylandWindowConfiguration * -meta_wayland_window_configuration_new (int x, - int y, - int width, - int height, - int scale, - MetaMoveResizeFlags flags, - MetaGravity gravity) +meta_wayland_window_configuration_new (MetaWindow *window, + MetaRectangle rect, + int bounds_width, + int bounds_height, + int scale, + MetaMoveResizeFlags flags, + MetaGravity gravity) { MetaWaylandWindowConfiguration *configuration; @@ -39,19 +55,33 @@ meta_wayland_window_configuration_new (int x, *configuration = (MetaWaylandWindowConfiguration) { .serial = ++global_serial_counter, - .has_position = TRUE, - .x = x, - .y = y, - - .has_size = TRUE, - .width = width, - .height = height, + .bounds_height = bounds_height, + .bounds_width = bounds_width, .scale = scale, .gravity = gravity, .flags = flags, }; + if (flags & META_MOVE_RESIZE_MOVE_ACTION || + window->rect.x != rect.x || + window->rect.y != rect.y) + { + configuration->has_position = TRUE; + configuration->x = rect.x; + configuration->y = rect.y; + } + + if (flags & META_MOVE_RESIZE_RESIZE_ACTION || + is_window_size_fixed (window) || + window->rect.width != rect.width || + window->rect.height != rect.height) + { + configuration->has_size = TRUE; + configuration->width = rect.width; + configuration->height = rect.height; + } + return configuration; } @@ -83,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; @@ -91,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 b524f431f..e161c4799 100644 --- a/src/wayland/meta-wayland-window-configuration.h +++ b/src/wayland/meta-wayland-window-configuration.h @@ -46,12 +46,15 @@ struct _MetaWaylandWindowConfiguration int scale; MetaGravity gravity; MetaMoveResizeFlags flags; + + int bounds_width; + int bounds_height; }; -MetaWaylandWindowConfiguration * meta_wayland_window_configuration_new (int x, - int y, - int width, - int height, +MetaWaylandWindowConfiguration * meta_wayland_window_configuration_new (MetaWindow *window, + MetaRectangle rect, + int max_width, + int max_height, int scale, MetaMoveResizeFlags flags, MetaGravity gravity); @@ -62,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 67733fb8e..355067fa5 100644 --- a/src/wayland/meta-wayland-xdg-shell.c +++ b/src/wayland/meta-wayland-xdg-shell.c @@ -696,6 +696,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, @@ -773,8 +785,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 ed030b937..0817ea1d2 100644 --- a/src/wayland/meta-window-wayland.c +++ b/src/wayland/meta-window-wayland.c @@ -53,10 +53,7 @@ struct _MetaWindowWayland GList *pending_configurations; gboolean has_pending_state_change; - int last_sent_x; - int last_sent_y; - int last_sent_width; - int last_sent_height; + MetaRectangle last_sent_rect; int last_sent_rel_x; int last_sent_rel_y; int last_sent_geometry_scale; @@ -183,16 +180,23 @@ 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) return; + if (!meta_window_calculate_bounds (window, &bounds_width, &bounds_height)) + { + bounds_width = 0; + bounds_height = 0; + } + configuration = - meta_wayland_window_configuration_new (wl_window->last_sent_x, - wl_window->last_sent_y, - wl_window->last_sent_width, - wl_window->last_sent_height, + 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); @@ -233,10 +237,7 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); gboolean can_move_now = FALSE; - int configured_x; - int configured_y; - int configured_width; - int configured_height; + MetaRectangle configured_rect; int geometry_scale; int new_x; int new_y; @@ -249,8 +250,8 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, if (window->unmanaging) return; - configured_x = constrained_rect.x; - configured_y = constrained_rect.y; + configured_rect.x = constrained_rect.x; + configured_rect.y = constrained_rect.y; /* The scale the window is drawn in might change depending on what monitor it * is mainly on. Scale the configured rectangle to be in logical pixel @@ -258,24 +259,8 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, * to the Wayland surface. */ geometry_scale = meta_window_wayland_get_geometry_scale (window); - if (flags & META_MOVE_RESIZE_UNMAXIMIZE && - !meta_window_is_fullscreen (window)) - { - configured_width = 0; - configured_height = 0; - } - else if (flags & META_MOVE_RESIZE_UNFULLSCREEN && - !meta_window_get_maximized (window) && - meta_window_get_tile_mode (window) == META_TILE_NONE) - { - configured_width = 0; - configured_height = 0; - } - else - { - configured_width = constrained_rect.width; - configured_height = constrained_rect.height; - } + configured_rect.width = constrained_rect.width; + configured_rect.height = constrained_rect.height; /* For wayland clients, the size is completely determined by the client, * and while this allows to avoid some trickery with frames and the resulting @@ -335,8 +320,8 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, configuration = meta_wayland_window_configuration_new_relative (rel_x, rel_y, - configured_width, - configured_height, + configured_rect.width, + configured_rect.height, geometry_scale); meta_window_wayland_configure (wl_window, configuration); @@ -366,29 +351,27 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, flags & META_MOVE_RESIZE_STATE_CHANGED) { MetaWaylandWindowConfiguration *configuration; + int bounds_width; + int bounds_height; - /* If the constrained size is 1x1 and the unconstrained size is 0x0 - * it means that we are trying to resize a window where the client has - * not yet committed a buffer. The 1x1 constrained size is a result of - * how the constraints code works. Lets avoid trying to have the - * client configure itself to draw on a 1x1 surface. - * - * We cannot guard against only an empty unconstrained_rect here, - * because the client may have created a xdg surface without a buffer - * attached and asked it to be maximized. In such case we should let - * it know about the expected window geometry of a maximized window, - * even though there is currently no buffer attached. */ - if (unconstrained_rect.width == 0 && - unconstrained_rect.height == 0 && - constrained_rect.width == 1 && - constrained_rect.height == 1) + if (!meta_wayland_surface_get_buffer (window->surface) && + !META_WINDOW_MAXIMIZED (window) && + window->tile_mode == META_TILE_NONE && + !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 (configured_x, - configured_y, - configured_width, - configured_height, + meta_wayland_window_configuration_new (window, + configured_rect, + bounds_width, bounds_height, geometry_scale, flags, gravity); @@ -401,10 +384,7 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, } } - wl_window->last_sent_x = configured_x; - wl_window->last_sent_y = configured_y; - wl_window->last_sent_width = configured_width; - wl_window->last_sent_height = configured_height; + wl_window->last_sent_rect = configured_rect; wl_window->last_sent_geometry_scale = geometry_scale; wl_window->last_sent_gravity = gravity; @@ -504,6 +484,9 @@ meta_window_wayland_update_main_monitor (MetaWindow *window, return; } + if (window->rect.width == 0 || window->rect.height == 0) + return; + /* Require both the current and the new monitor would be the new main monitor, * even given the resulting scale the window would end up having. This is * needed to avoid jumping back and forth between the new and the old, since @@ -971,6 +954,13 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, is_window_being_resized = (meta_grab_op_is_resizing (display->grab_op) && display->grab_window == window); + rect = (MetaRectangle) { + .x = window->rect.x, + .y = window->rect.y, + .width = new_geom.width, + .height = new_geom.height + }; + if (!is_window_being_resized) { if (acked_configuration) @@ -983,26 +973,21 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, rect.x = parent->rect.x + acked_configuration->rel_x; rect.y = parent->rect.y + acked_configuration->rel_y; } - else + else if (acked_configuration->has_position) { calculate_offset (acked_configuration, &new_geom, &rect); } } - else - { - rect.x = window->rect.x; - rect.y = window->rect.y; - } - - rect.x += dx; - rect.y += dy; } else { - if (acked_configuration) + if (acked_configuration && acked_configuration->has_position) calculate_offset (acked_configuration, &new_geom, &rect); } + rect.x += dx; + rect.y += dy; + if (rect.x != window->rect.x || rect.y != window->rect.y) flags |= META_MOVE_RESIZE_MOVE_ACTION; @@ -1012,9 +997,6 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, wl_window->has_pending_state_change = FALSE; } - rect.width = new_geom.width; - rect.height = new_geom.height; - if (rect.width != window->rect.width || rect.height != window->rect.height) flags |= META_MOVE_RESIZE_RESIZE_ACTION;