From 24eaad6108d5bccc093e40cbe7d03800b7a31bf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 29 Apr 2025 14:26:15 +0200 Subject: [PATCH 01/38] ci: Install glib from main This is needed for https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4615 Part-of: --- .gitlab-ci.yml | 2 +- .gitlab-ci/install-common-dependencies.sh | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a22fde97eb6..0ba09cc4fe4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -106,7 +106,7 @@ variables: - .skip-git-clone variables: FDO_DISTRIBUTION_VERSION: 42 - BASE_TAG: '2025-04-08.0' + BASE_TAG: '2025-04-29.0' MUTTER_USER: 'meta-user' FDO_DISTRIBUTION_PACKAGES: accountsservice-devel diff --git a/.gitlab-ci/install-common-dependencies.sh b/.gitlab-ci/install-common-dependencies.sh index 83bb07bfec3..7a90881c5ea 100755 --- a/.gitlab-ci/install-common-dependencies.sh +++ b/.gitlab-ci/install-common-dependencies.sh @@ -131,3 +131,8 @@ then https://gitlab.freedesktop.org/wayland/wayland-protocols.git \ 1.43 fi + +./$SCRIPTS_DIR/install-meson-project.sh \ + "${OPTIONS[@]}" \ + https://gitlab.gnome.org/GNOME/glib.git \ + main c22642589b5f999d6b89379c852c199919da6ae5 -- GitLab From 6d0fa51764190fdffa0a8fecfe2b82c44b97a84d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 17 Apr 2025 22:19:11 +0200 Subject: [PATCH 02/38] window: Move maximized state into window config This will make it easier to eventually allow configuring maximized state via the ::configure signal. Part-of: --- src/compositor/meta-window-drag.c | 6 +- src/core/constraints.c | 34 ++++++---- src/core/display.c | 3 +- src/core/keybindings.c | 15 +++-- src/core/meta-window-config-private.h | 12 ++++ src/core/meta-window-config.c | 36 ++++++++++ src/core/window-private.h | 4 -- src/core/window.c | 94 ++++++++++++++++++--------- src/x11/window-props.c | 4 +- src/x11/window-x11.c | 11 ++-- 10 files changed, 154 insertions(+), 65 deletions(-) diff --git a/src/compositor/meta-window-drag.c b/src/compositor/meta-window-drag.c index c8a4c335abf..360d14a3ceb 100644 --- a/src/compositor/meta-window-drag.c +++ b/src/compositor/meta-window-drag.c @@ -23,6 +23,7 @@ #include "compositor/compositor-private.h" #include "compositor/edge-resistance.h" +#include "core/meta-window-config-private.h" #include "core/window-private.h" #include "meta/meta-enum-types.h" @@ -1369,9 +1370,10 @@ update_move (MetaWindowDrag *window_drag, meta_window_get_frame_rect (window, &old); /* Don't allow movement in the maximized directions or while tiled */ - if (window->maximized_horizontally || meta_window_is_tiled_side_by_side (window)) + if (meta_window_config_is_maximized_horizontally (window->config) || + meta_window_is_tiled_side_by_side (window)) new_x = old.x; - if (window->maximized_vertically) + if (meta_window_config_is_maximized_vertically (window->config)) new_y = old.y; window_drag->last_edge_resistance_flags = diff --git a/src/core/constraints.c b/src/core/constraints.c index 9c287c668d4..a1bc051a930 100644 --- a/src/core/constraints.c +++ b/src/core/constraints.c @@ -33,6 +33,7 @@ #include "backends/meta-monitor-manager-private.h" #include "compositor/compositor-private.h" #include "core/boxes-private.h" +#include "core/meta-window-config-private.h" #include "core/meta-workspace-manager-private.h" #include "core/place.h" #include "core/workspace-private.h" @@ -549,8 +550,7 @@ place_window_if_needed (MetaWindow *window, did_placement = FALSE; if (!window->placed && window->calc_placement && - !(window->maximized_horizontally || - window->maximized_vertically) && + !meta_window_config_is_any_maximized (window->config) && !window->minimized && !meta_window_is_fullscreen (window)) { @@ -1211,6 +1211,7 @@ constrain_maximization (MetaWindow *window, gboolean check_only) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; + gboolean is_maximized_horizontally, is_maximized_vertically; MtkRectangle target_size; MtkRectangle min_size, max_size; gboolean hminbad, vminbad; @@ -1221,10 +1222,15 @@ constrain_maximization (MetaWindow *window, return TRUE; /* Determine whether constraint applies; exit if it doesn't */ - if ((!window->maximized_horizontally && !window->maximized_vertically) || + if (!meta_window_config_is_any_maximized (window->config) || meta_window_is_tiled_side_by_side (window)) return TRUE; + is_maximized_horizontally = + meta_window_config_is_maximized_horizontally (window->config); + is_maximized_vertically = + meta_window_config_is_maximized_vertically (window->config); + /* Calculate target_size = maximized size of (window + frame) */ if (meta_window_is_maximized (window) && window->tile_mode == META_TILE_MAXIMIZED) @@ -1247,7 +1253,7 @@ constrain_maximization (MetaWindow *window, MetaDirection direction; GSList *active_workspace_struts; - if (window->maximized_horizontally) + if (is_maximized_horizontally) direction = META_DIRECTION_HORIZONTAL; else direction = META_DIRECTION_VERTICAL; @@ -1264,8 +1270,8 @@ constrain_maximization (MetaWindow *window, * windows, as per bug 327543. */ get_size_limits (window, &min_size, &max_size); - hminbad = target_size.width < min_size.width && window->maximized_horizontally; - vminbad = target_size.height < min_size.height && window->maximized_vertically; + hminbad = target_size.width < min_size.width && is_maximized_horizontally; + vminbad = target_size.height < min_size.height && is_maximized_vertically; if (hminbad || vminbad) return TRUE; @@ -1275,18 +1281,18 @@ constrain_maximization (MetaWindow *window, vert_equal = target_size.y == info->current.y && target_size.height == info->current.height; constraint_already_satisfied = - (horiz_equal || !window->maximized_horizontally) && - (vert_equal || !window->maximized_vertically); + (horiz_equal || !is_maximized_horizontally) && + (vert_equal || !is_maximized_vertically); if (check_only || constraint_already_satisfied) return constraint_already_satisfied; /*** Enforce constraint ***/ - if (window->maximized_horizontally) + if (is_maximized_horizontally) { info->current.x = target_size.x; info->current.width = target_size.width; } - if (window->maximized_vertically) + if (is_maximized_vertically) { info->current.y = target_size.y; info->current.height = target_size.height; @@ -1413,9 +1419,9 @@ constrain_size_increments (MetaWindow *window, extra_height = (client_rect.height - bh) % hi; extra_width = (client_rect.width - bw) % wi; /* ignore size increments for maximized windows */ - if (window->maximized_horizontally) + if (meta_window_config_is_maximized_horizontally (window->config)) extra_width *= 0; - if (window->maximized_vertically) + if (meta_window_config_is_maximized_vertically (window->config)) extra_height *= 0; /* constraint is satisfied iff there is no extra height or width */ constraint_already_satisfied = @@ -1481,9 +1487,9 @@ constrain_size_limits (MetaWindow *window, /* Determine whether constraint is already satisfied; exit if it is */ get_size_limits (window, &min_size, &max_size); /* We ignore max-size limits for maximized windows; see #327543 */ - if (window->maximized_horizontally) + if (meta_window_config_is_maximized_horizontally (window->config)) max_size.width = MAX (max_size.width, info->current.width); - if (window->maximized_vertically) + if (meta_window_config_is_maximized_vertically (window->config)) max_size.height = MAX (max_size.height, info->current.height); too_small = !mtk_rectangle_could_fit_rect (&info->current, &min_size); too_big = !mtk_rectangle_could_fit_rect (&max_size, &info->current); diff --git a/src/core/display.c b/src/core/display.c index c2600ec753d..b7731197edc 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -3101,8 +3101,7 @@ check_fullscreen_func (gpointer data) if (meta_window_is_monitor_sized (window)) covers_monitors = TRUE; } - else if (window->maximized_horizontally && - window->maximized_vertically) + else if (meta_window_is_maximized (window)) { MetaLogicalMonitor *logical_monitor; diff --git a/src/core/keybindings.c b/src/core/keybindings.c index 32b143b70d8..8aee613f084 100644 --- a/src/core/keybindings.c +++ b/src/core/keybindings.c @@ -36,6 +36,7 @@ #include "compositor/compositor-private.h" #include "core/keybindings-private.h" #include "core/meta-accel-parse.h" +#include "core/meta-window-config-private.h" #include "core/meta-workspace-manager-private.h" #include "core/workspace-private.h" #include "meta/compositor.h" @@ -1790,7 +1791,7 @@ handle_maximize_vertically (MetaDisplay *display, { if (window->has_resize_func) { - if (window->maximized_vertically) + if (meta_window_config_is_maximized_vertically (window->config)) meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL); else meta_window_maximize (window, META_MAXIMIZE_VERTICAL); @@ -1806,7 +1807,7 @@ handle_maximize_horizontally (MetaDisplay *display, { if (window->has_resize_func) { - if (window->maximized_horizontally) + if (meta_window_config_is_maximized_horizontally (window->config)) meta_window_unmaximize (window, META_MAXIMIZE_HORIZONTAL); else meta_window_maximize (window, META_MAXIMIZE_HORIZONTAL); @@ -2125,6 +2126,8 @@ handle_toggle_tiled (MetaDisplay *display, } else if (meta_window_can_tile_side_by_side (window, window->monitor->number)) { + gboolean is_maximized_vertically; + window->tile_monitor_number = window->monitor->number; /* Maximization constraints beat tiling constraints, so if the window * is maximized, tiling won't have any effect unless we unmaximize it @@ -2132,7 +2135,11 @@ handle_toggle_tiled (MetaDisplay *display, * we just set the flag and rely on meta_window_tile() syncing it to * save an additional roundtrip. */ - window->maximized_horizontally = FALSE; + is_maximized_vertically = + meta_window_config_is_maximized_vertically (window->config); + meta_window_config_set_maximized_directions (window->config, + FALSE, + is_maximized_vertically); meta_window_tile (window, mode); } } @@ -2168,7 +2175,7 @@ handle_unmaximize (MetaDisplay *display, MetaKeyBinding *binding, gpointer user_data) { - if (window->maximized_vertically || window->maximized_horizontally) + if (meta_window_config_is_any_maximized (window->config)) meta_window_unmaximize (window, META_MAXIMIZE_BOTH); } diff --git a/src/core/meta-window-config-private.h b/src/core/meta-window-config-private.h index ea34b69998d..6a56540009b 100644 --- a/src/core/meta-window-config-private.h +++ b/src/core/meta-window-config-private.h @@ -21,3 +21,15 @@ #include "meta/meta-window-config.h" MetaWindowConfig * meta_window_config_initial_new (void); + +gboolean meta_window_config_is_maximized (MetaWindowConfig *config); + +gboolean meta_window_config_is_any_maximized (MetaWindowConfig *config); + +gboolean meta_window_config_is_maximized_horizontally (MetaWindowConfig *config); + +gboolean meta_window_config_is_maximized_vertically (MetaWindowConfig *config); + +void meta_window_config_set_maximized_directions (MetaWindowConfig *window_config, + gboolean horizontally, + gboolean vertically); diff --git a/src/core/meta-window-config.c b/src/core/meta-window-config.c index 74f5aa81f4c..4f73b3e29f1 100644 --- a/src/core/meta-window-config.c +++ b/src/core/meta-window-config.c @@ -37,6 +37,9 @@ struct _MetaWindowConfig MtkRectangle rect; gboolean is_fullscreen; + + gboolean maximized_horizontally; + gboolean maximized_vertically; }; G_DEFINE_FINAL_TYPE (MetaWindowConfig, meta_window_config, G_TYPE_OBJECT) @@ -200,6 +203,39 @@ meta_window_config_get_is_fullscreen (MetaWindowConfig *window_config) return window_config->is_fullscreen; } +gboolean +meta_window_config_is_maximized (MetaWindowConfig *config) +{ + return config->maximized_horizontally && config->maximized_vertically; +} + +gboolean +meta_window_config_is_any_maximized (MetaWindowConfig *config) +{ + return config->maximized_horizontally || config->maximized_vertically; +} + +gboolean +meta_window_config_is_maximized_horizontally (MetaWindowConfig *config) +{ + return config->maximized_horizontally; +} + +gboolean +meta_window_config_is_maximized_vertically (MetaWindowConfig *config) +{ + return config->maximized_vertically; +} + +void +meta_window_config_set_maximized_directions (MetaWindowConfig *config, + gboolean horizontally, + gboolean vertically) +{ + config->maximized_horizontally = horizontally; + config->maximized_vertically = vertically; +} + MetaWindowConfig * meta_window_config_new (void) { diff --git a/src/core/window-private.h b/src/core/window-private.h index c48a60aacc7..d5e5f292ee5 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -392,10 +392,6 @@ struct _MetaWindow /* Whether this is an override redirect window or not */ guint override_redirect : 1; - /* Whether we're maximized */ - guint maximized_horizontally : 1; - guint maximized_vertically : 1; - /* Whether we have to maximize/minimize after placement */ guint maximize_horizontally_after_placement : 1; guint maximize_vertically_after_placement : 1; diff --git a/src/core/window.c b/src/core/window.c index a463ddbbc0c..85e4015ee52 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -389,6 +389,7 @@ meta_window_get_property(GObject *object, { MetaWindow *win = META_WINDOW (object); MetaWindowPrivate *priv = meta_window_get_instance_private (win); + MetaWindowConfig *config = win->config; switch (prop_id) { @@ -402,10 +403,12 @@ meta_window_get_property(GObject *object, g_value_set_boolean (value, meta_window_is_fullscreen (win)); break; case PROP_MAXIMIZED_HORIZONTALLY: - g_value_set_boolean (value, win->maximized_horizontally); + g_value_set_boolean (value, + meta_window_config_is_maximized_horizontally (config)); break; case PROP_MAXIMIZED_VERTICALLY: - g_value_set_boolean (value, win->maximized_vertically); + g_value_set_boolean (value, + meta_window_config_is_maximized_vertically (config)); break; case PROP_MINIMIZED: g_value_set_boolean (value, win->minimized); @@ -1111,8 +1114,6 @@ meta_window_constructed (GObject *object) window->has_focus = FALSE; window->attached_focus_window = NULL; - window->maximized_horizontally = FALSE; - window->maximized_vertically = FALSE; window->maximize_horizontally_after_placement = FALSE; window->maximize_vertically_after_placement = FALSE; window->minimize_after_placement = FALSE; @@ -1595,7 +1596,7 @@ meta_window_unmanage (MetaWindow *window, invalidate_work_areas (window); } - if (window->maximized_horizontally || window->maximized_vertically) + if (meta_window_config_is_any_maximized (window->config)) unmaximize_window_before_freeing (window); meta_window_unqueue (window, @@ -2791,12 +2792,12 @@ meta_window_save_rect (MetaWindow *window) frame_rect = meta_window_config_get_rect (window->config); /* save size/pos as appropriate args for move_resize */ - if (!window->maximized_horizontally) + if (!meta_window_config_is_maximized_horizontally (window->config)) { window->saved_rect.x = frame_rect.x; window->saved_rect.width = frame_rect.width; } - if (!window->maximized_vertically) + if (!meta_window_config_is_maximized_vertically (window->config)) { window->saved_rect.y = frame_rect.y; window->saved_rect.height = frame_rect.height; @@ -2811,8 +2812,11 @@ meta_window_maximize_internal (MetaWindow *window, { /* At least one of the two directions ought to be set */ gboolean maximize_horizontally, maximize_vertically; + gboolean was_maximized_horizontally, was_maximized_vertically; + maximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL; - maximize_vertically = directions & META_MAXIMIZE_VERTICAL; + maximize_vertically = directions & META_MAXIMIZE_VERTICAL; + g_assert (maximize_horizontally || maximize_vertically); meta_topic (META_DEBUG_WINDOW_OPS, @@ -2822,6 +2826,11 @@ meta_window_maximize_internal (MetaWindow *window, maximize_horizontally ? " horizontally" : maximize_vertically ? " vertically" : "BUGGGGG"); + was_maximized_horizontally = + meta_window_config_is_maximized_horizontally (window->config); + was_maximized_vertically = + meta_window_config_is_maximized_vertically (window->config); + if (saved_rect != NULL) window->saved_rect = *saved_rect; else @@ -2830,10 +2839,10 @@ meta_window_maximize_internal (MetaWindow *window, if (maximize_horizontally && maximize_vertically) window->saved_maximize = TRUE; - window->maximized_horizontally = - window->maximized_horizontally || maximize_horizontally; - window->maximized_vertically = - window->maximized_vertically || maximize_vertically; + meta_window_config_set_maximized_directions ( + window->config, + was_maximized_horizontally || maximize_horizontally, + was_maximized_vertically || maximize_vertically); /* Update the edge constraints */ update_edge_constraints (window); @@ -2856,6 +2865,7 @@ meta_window_maximize (MetaWindow *window, { MtkRectangle *saved_rect = NULL; gboolean maximize_horizontally, maximize_vertically; + gboolean was_maximized_horizontally, was_maximized_vertically; g_return_if_fail (META_IS_WINDOW (window)); g_return_if_fail (!window->override_redirect); @@ -2865,11 +2875,16 @@ meta_window_maximize (MetaWindow *window, maximize_vertically = directions & META_MAXIMIZE_VERTICAL; g_assert (maximize_horizontally || maximize_vertically); + was_maximized_horizontally = + meta_window_config_is_maximized_horizontally (window->config); + was_maximized_vertically = + meta_window_config_is_maximized_vertically (window->config); + /* Only do something if the window isn't already maximized in the * given direction(s). */ - if ((maximize_horizontally && !window->maximized_horizontally) || - (maximize_vertically && !window->maximized_vertically)) + if ((maximize_horizontally && !was_maximized_horizontally) || + (maximize_vertically && !was_maximized_vertically)) { /* if the window hasn't been placed yet, we'll maximize it then */ @@ -2888,7 +2903,9 @@ meta_window_maximize (MetaWindow *window, { saved_rect = &window->saved_rect; - window->maximized_vertically = FALSE; + meta_window_config_set_maximized_directions (window->config, + was_maximized_horizontally, + FALSE); window->tile_mode = META_TILE_NONE; } @@ -2926,8 +2943,14 @@ meta_window_maximize (MetaWindow *window, MetaMaximizeFlags meta_window_get_maximized (MetaWindow *window) { - return ((window->maximized_horizontally ? META_MAXIMIZE_HORIZONTAL : 0) | - (window->maximized_vertically ? META_MAXIMIZE_VERTICAL : 0)); + MetaMaximizeFlags flags = 0; + + if (meta_window_config_is_maximized_horizontally (window->config)) + flags |= META_MAXIMIZE_HORIZONTAL; + if (meta_window_config_is_maximized_vertically (window->config)) + flags |= META_MAXIMIZE_VERTICAL; + + return flags; } /** @@ -2939,7 +2962,7 @@ meta_window_get_maximized (MetaWindow *window) gboolean meta_window_is_maximized (MetaWindow *window) { - return (window->maximized_horizontally && window->maximized_vertically); + return meta_window_config_is_maximized (window->config); } /** @@ -3124,13 +3147,13 @@ update_edge_constraints (MetaWindow *window) } /* h/vmaximize also modify the edge constraints */ - if (window->maximized_vertically) + if (meta_window_config_is_maximized_vertically (window->config)) { window->edge_constraints.top = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.bottom = META_EDGE_CONSTRAINT_MONITOR; } - if (window->maximized_horizontally) + if (meta_window_config_is_maximized_horizontally (window->config)) { window->edge_constraints.right = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.left = META_EDGE_CONSTRAINT_MONITOR; @@ -3140,9 +3163,11 @@ update_edge_constraints (MetaWindow *window) gboolean meta_window_is_tiled_side_by_side (MetaWindow *window) { - return window->maximized_vertically && - !window->maximized_horizontally && - window->tile_mode != META_TILE_NONE; + MetaWindowConfig *config = window->config; + + return (meta_window_config_is_maximized_vertically (config) && + !meta_window_config_is_maximized_horizontally (config) && + window->tile_mode != META_TILE_NONE); } gboolean @@ -3279,8 +3304,7 @@ unmaximize_window_before_freeing (MetaWindow *window) "Unmaximizing %s just before freeing", window->desc); - window->maximized_horizontally = FALSE; - window->maximized_vertically = FALSE; + meta_window_config_set_maximized_directions (window->config, FALSE, FALSE); if (window->withdrawn) /* See bug #137185 */ { @@ -3320,6 +3344,7 @@ meta_window_unmaximize (MetaWindow *window, MetaMaximizeFlags directions) { gboolean unmaximize_horizontally, unmaximize_vertically; + gboolean was_maximized_horizontally, was_maximized_vertically; g_return_if_fail (META_IS_WINDOW (window)); g_return_if_fail (!window->override_redirect); @@ -3332,11 +3357,16 @@ meta_window_unmaximize (MetaWindow *window, if (unmaximize_horizontally && unmaximize_vertically) window->saved_maximize = FALSE; + was_maximized_horizontally = + meta_window_config_is_maximized_horizontally (window->config); + was_maximized_vertically = + meta_window_config_is_maximized_vertically (window->config); + /* Only do something if the window isn't already maximized in the * given direction(s). */ - if ((unmaximize_horizontally && window->maximized_horizontally) || - (unmaximize_vertically && window->maximized_vertically)) + if ((unmaximize_horizontally && was_maximized_horizontally) || + (unmaximize_vertically && was_maximized_vertically)) { MtkRectangle *desired_rect; MtkRectangle target_rect; @@ -3358,10 +3388,10 @@ meta_window_unmaximize (MetaWindow *window, unmaximize_horizontally ? " horizontally" : unmaximize_vertically ? " vertically" : "BUGGGGG"); - window->maximized_horizontally = - window->maximized_horizontally && !unmaximize_horizontally; - window->maximized_vertically = - window->maximized_vertically && !unmaximize_vertically; + meta_window_config_set_maximized_directions ( + window->config, + was_maximized_horizontally && !unmaximize_horizontally, + was_maximized_vertically && !unmaximize_vertically); /* Update the edge constraints */ update_edge_constraints (window); @@ -4207,7 +4237,7 @@ meta_window_move_resize_internal (MetaWindow *window, if (flags & META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE && (result & META_MOVE_RESIZE_RESULT_MOVED || result & META_MOVE_RESIZE_RESULT_RESIZED) && - (window->maximized_horizontally || window->maximized_vertically)) + meta_window_config_is_any_maximized (window->config)) meta_window_queue (window, META_QUEUE_MOVE_RESIZE); } diff --git a/src/x11/window-props.c b/src/x11/window-props.c index af6506d6b20..3c49e3a1cb3 100644 --- a/src/x11/window-props.c +++ b/src/x11/window-props.c @@ -44,6 +44,7 @@ #include #include "compositor/compositor-private.h" +#include "core/meta-window-config-private.h" #include "core/meta-workspace-manager-private.h" #include "core/util-private.h" #include "meta/meta-x11-group.h" @@ -804,8 +805,7 @@ reload_net_wm_state (MetaWindow *window, return; } - window->maximized_horizontally = FALSE; - window->maximized_vertically = FALSE; + meta_window_config_set_maximized_directions (window->config, FALSE, FALSE); meta_window_config_set_is_fullscreen (window->config, FALSE); priv->wm_state_modal = FALSE; priv->wm_state_skip_taskbar = FALSE; diff --git a/src/x11/window-x11.c b/src/x11/window-x11.c index ebcb97c2cc5..fc0f88a9f39 100644 --- a/src/x11/window-x11.c +++ b/src/x11/window-x11.c @@ -2369,12 +2369,12 @@ meta_window_x11_set_net_wm_state (MetaWindow *window) data[i] = x11_display->atom__NET_WM_STATE_SKIP_TASKBAR; ++i; } - if (window->maximized_horizontally) + if (meta_window_config_is_maximized_horizontally (window->config)) { data[i] = x11_display->atom__NET_WM_STATE_MAXIMIZED_HORZ; ++i; } - if (window->maximized_vertically) + if (meta_window_config_is_maximized_vertically (window->config)) { data[i] = x11_display->atom__NET_WM_STATE_MAXIMIZED_VERT; ++i; @@ -3461,9 +3461,10 @@ meta_window_x11_client_message (MetaWindow *window, gboolean max; MetaMaximizeFlags directions = 0; - max = (action == _NET_WM_STATE_ADD || - (action == _NET_WM_STATE_TOGGLE && - !window->maximized_horizontally)); + max = + (action == _NET_WM_STATE_ADD || + (action == _NET_WM_STATE_TOGGLE && + !meta_window_config_is_maximized_horizontally (window->config))); if (first == x11_display->atom__NET_WM_STATE_MAXIMIZED_HORZ || second == x11_display->atom__NET_WM_STATE_MAXIMIZED_HORZ) -- GitLab From 3f1c794ed5b174e197e065bbb2c6756b5f53bbb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 17 Apr 2025 22:23:39 +0200 Subject: [PATCH 03/38] window: Move tile state to the window config This will make it easier to eventually allow overriding this via the configure signal. Side effects, such as edge constraints, are left out. Part-of: --- src/compositor/meta-window-drag.c | 36 ++-- src/core/constraints.c | 10 +- src/core/display-private.h | 8 - src/core/keybindings.c | 3 +- src/core/meta-window-config-private.h | 29 ++++ src/core/meta-window-config.c | 59 +++++++ src/core/window-private.h | 11 +- src/core/window.c | 163 ++++++++++++------- src/wayland/meta-wayland-gtk-shell.c | 5 +- src/wayland/meta-wayland-xdg-session-state.c | 16 +- src/wayland/meta-window-wayland.c | 4 +- 11 files changed, 246 insertions(+), 98 deletions(-) diff --git a/src/compositor/meta-window-drag.c b/src/compositor/meta-window-drag.c index 360d14a3ceb..20ba53c706e 100644 --- a/src/compositor/meta-window-drag.c +++ b/src/compositor/meta-window-drag.c @@ -568,7 +568,8 @@ process_mouse_move_resize_grab (MetaWindowDrag *window_drag, /* Restore the original tile mode */ tile_mode = window_drag->tile_mode; - window->tile_monitor_number = window_drag->tile_monitor_number; + meta_window_config_set_tile_monitor_number (window->config, + window_drag->tile_monitor_number); /* End move or resize and restore to original state. If the * window was a maximized window that had been "shaken loose" we @@ -1185,7 +1186,10 @@ update_move_maybe_tile (MetaWindowDrag *window_drag, window_drag->preview_tile_mode = META_TILE_NONE; if (window_drag->preview_tile_mode != META_TILE_NONE) - window->tile_monitor_number = logical_monitor->number; + { + meta_window_config_set_tile_monitor_number (window->config, + logical_monitor->number); + } } static void @@ -1245,7 +1249,7 @@ update_move (MetaWindowDrag *window_drag, /* We don't want to tile while snapping. Also, clear any previous tile request. */ window_drag->preview_tile_mode = META_TILE_NONE; - window->tile_monitor_number = -1; + meta_window_config_set_tile_monitor_number (window->config, -1); } else if (meta_prefs_get_edge_tiling () && !meta_window_is_maximized (window) && @@ -1270,7 +1274,7 @@ update_move (MetaWindowDrag *window_drag, * is enabled, as top edge tiling can be used in that case */ window_drag->shaken_loose = !meta_prefs_get_edge_tiling (); - window->tile_mode = META_TILE_NONE; + meta_window_config_set_tile_mode (window->config, META_TILE_NONE); /* move the unmaximized window to the cursor */ prop = @@ -1297,7 +1301,8 @@ update_move (MetaWindowDrag *window_drag, * loose or it is still maximized (then move straight) */ else if ((window_drag->shaken_loose || meta_window_is_maximized (window)) && - window->tile_mode != META_TILE_LEFT && window->tile_mode != META_TILE_RIGHT) + meta_window_config_get_tile_mode (window->config) != META_TILE_LEFT && + meta_window_config_get_tile_mode (window->config) != META_TILE_RIGHT) { MetaDisplay *display = meta_window_get_display (window); MetaContext *context = meta_display_get_context (display); @@ -1312,7 +1317,7 @@ update_move (MetaWindowDrag *window_drag, MetaFrame *frame; #endif - window->tile_mode = META_TILE_NONE; + meta_window_config_set_tile_mode (window->config, META_TILE_NONE); wmonitor = window->monitor; n_logical_monitors = meta_monitor_manager_get_num_logical_monitors (monitor_manager); @@ -1365,7 +1370,9 @@ update_move (MetaWindowDrag *window_drag, * trigger it unwittingly, e.g. when shaking loose the window or moving * it to another monitor. */ - update_tile_preview (window_drag, window->tile_mode != META_TILE_NONE); + update_tile_preview (window_drag, + meta_window_config_get_tile_mode (window->config) != + META_TILE_NONE); meta_window_get_frame_rect (window, &old); @@ -1600,6 +1607,7 @@ maybe_maximize_tiled_window (MetaWindow *window) { MtkRectangle work_area; gint shake_threshold; + int tile_monitor_number; int width; if (!meta_window_is_tiled_side_by_side (window)) @@ -1607,8 +1615,10 @@ maybe_maximize_tiled_window (MetaWindow *window) shake_threshold = meta_prefs_get_drag_threshold (); + tile_monitor_number = + meta_window_config_get_tile_monitor_number (window->config); meta_window_get_work_area_for_monitor (window, - window->tile_monitor_number, + tile_monitor_number, &work_area); meta_window_config_get_size (window->config, &width, NULL); if (width >= work_area.width - shake_threshold) @@ -1673,7 +1683,7 @@ end_grab_op (MetaWindowDrag *window_drag, } else if (meta_grab_op_is_resizing (window_drag->grab_op)) { - if (window->tile_match != NULL) + if (meta_window_config_get_tile_match (window->config)) flags |= (META_EDGE_RESISTANCE_SNAP | META_EDGE_RESISTANCE_WINDOWS); update_resize (window_drag, flags, (int) x, (int) y); @@ -1748,7 +1758,7 @@ process_pointer_event (MetaWindowDrag *window_drag, } else if (meta_grab_op_is_resizing (window_drag->grab_op)) { - if (window->tile_match != NULL) + if (meta_window_config_get_tile_match (window->config)) flags |= (META_EDGE_RESISTANCE_SNAP | META_EDGE_RESISTANCE_WINDOWS); queue_update_resize (window_drag, flags, (int) x, (int) y); @@ -1891,8 +1901,10 @@ meta_window_drag_begin (MetaWindowDrag *window_drag, window_drag->leading_device = device; window_drag->leading_touch_sequence = sequence; - window_drag->tile_mode = grab_window->tile_mode; - window_drag->tile_monitor_number = grab_window->tile_monitor_number; + window_drag->tile_mode = + meta_window_config_get_tile_mode (grab_window->config); + window_drag->tile_monitor_number = + meta_window_config_get_tile_monitor_number (grab_window->config); window_drag->anchor_root_x = root_x; window_drag->anchor_root_y = root_y; window_drag->latest_motion_x = root_x; diff --git a/src/core/constraints.c b/src/core/constraints.c index a1bc051a930..fa7d2e0d6b9 100644 --- a/src/core/constraints.c +++ b/src/core/constraints.c @@ -1217,6 +1217,7 @@ constrain_maximization (MetaWindow *window, gboolean hminbad, vminbad; gboolean horiz_equal, vert_equal; gboolean constraint_already_satisfied; + MetaTileMode tile_mode; if (priority > PRIORITY_MAXIMIZATION) return TRUE; @@ -1232,10 +1233,11 @@ constrain_maximization (MetaWindow *window, meta_window_config_is_maximized_vertically (window->config); /* Calculate target_size = maximized size of (window + frame) */ + tile_mode = meta_window_config_get_tile_mode (window->config); if (meta_window_is_maximized (window) && - window->tile_mode == META_TILE_MAXIMIZED) + tile_mode == META_TILE_MAXIMIZED) { - meta_window_get_tile_area (window, window->tile_mode, &target_size); + meta_window_get_tile_area (window, tile_mode, &target_size); } else if (meta_window_is_maximized (window)) { @@ -1306,6 +1308,7 @@ constrain_tiling (MetaWindow *window, ConstraintPriority priority, gboolean check_only) { + MetaTileMode tile_mode; MtkRectangle target_size; MtkRectangle min_size, max_size; gboolean hminbad, vminbad; @@ -1322,7 +1325,8 @@ constrain_tiling (MetaWindow *window, /* Calculate target_size - as the tile previews need this as well, we * use an external function for the actual calculation */ - meta_window_get_tile_area (window, window->tile_mode, &target_size); + tile_mode = meta_window_config_get_tile_mode (window->config); + meta_window_get_tile_area (window, tile_mode, &target_size); /* Check min size constraints; max size constraints are ignored as for * maximized windows. diff --git a/src/core/display-private.h b/src/core/display-private.h index bcfee4918a1..4881088947b 100644 --- a/src/core/display-private.h +++ b/src/core/display-private.h @@ -58,14 +58,6 @@ typedef enum #define _NET_WM_STATE_ADD 1 /* add/set property */ #define _NET_WM_STATE_TOGGLE 2 /* toggle property */ -typedef enum -{ - META_TILE_NONE, - META_TILE_LEFT, - META_TILE_RIGHT, - META_TILE_MAXIMIZED -} MetaTileMode; - typedef void (* MetaDisplayWindowFunc) (MetaWindow *window, gpointer user_data); diff --git a/src/core/keybindings.c b/src/core/keybindings.c index 8aee613f084..5af53703c0b 100644 --- a/src/core/keybindings.c +++ b/src/core/keybindings.c @@ -2128,7 +2128,8 @@ handle_toggle_tiled (MetaDisplay *display, { gboolean is_maximized_vertically; - window->tile_monitor_number = window->monitor->number; + meta_window_config_set_tile_monitor_number (window->config, + window->monitor->number); /* Maximization constraints beat tiling constraints, so if the window * is maximized, tiling won't have any effect unless we unmaximize it * horizontally first; rather than calling meta_window_unmaximize(), diff --git a/src/core/meta-window-config-private.h b/src/core/meta-window-config-private.h index 6a56540009b..93cf0c73aad 100644 --- a/src/core/meta-window-config-private.h +++ b/src/core/meta-window-config-private.h @@ -19,6 +19,15 @@ #pragma once #include "meta/meta-window-config.h" +#include "meta/types.h" + +typedef enum +{ + META_TILE_NONE, + META_TILE_LEFT, + META_TILE_RIGHT, + META_TILE_MAXIMIZED +} MetaTileMode; MetaWindowConfig * meta_window_config_initial_new (void); @@ -33,3 +42,23 @@ gboolean meta_window_config_is_maximized_vertically (MetaWindowConfig *config); void meta_window_config_set_maximized_directions (MetaWindowConfig *window_config, gboolean horizontally, gboolean vertically); + +MetaTileMode meta_window_config_get_tile_mode (MetaWindowConfig *config); + +int meta_window_config_get_tile_monitor_number (MetaWindowConfig *config); + +double meta_window_config_get_tile_hfraction (MetaWindowConfig *config); + +MetaWindow * meta_window_config_get_tile_match (MetaWindowConfig *config); + +void meta_window_config_set_tile_mode (MetaWindowConfig *config, + MetaTileMode tile_mode); + +void meta_window_config_set_tile_monitor_number (MetaWindowConfig *config, + int tile_monitor_number); + +void meta_window_config_set_tile_hfraction (MetaWindowConfig *config, + double hfraction); + +void meta_window_config_set_tile_match (MetaWindowConfig *config, + MetaWindow *tile_match); diff --git a/src/core/meta-window-config.c b/src/core/meta-window-config.c index 4f73b3e29f1..7948c6c2c64 100644 --- a/src/core/meta-window-config.c +++ b/src/core/meta-window-config.c @@ -40,6 +40,11 @@ struct _MetaWindowConfig gboolean maximized_horizontally; gboolean maximized_vertically; + + MetaTileMode tile_mode; + int tile_monitor_number; + double tile_hfraction; + MetaWindow *tile_match; }; G_DEFINE_FINAL_TYPE (MetaWindowConfig, meta_window_config, G_TYPE_OBJECT) @@ -129,6 +134,8 @@ static void meta_window_config_init (MetaWindowConfig *window_config) { window_config->rect = MTK_RECTANGLE_INIT (0, 0, 0, 0); + window_config->tile_monitor_number = -1; + window_config->tile_hfraction = -1.0; } gboolean @@ -236,6 +243,58 @@ meta_window_config_set_maximized_directions (MetaWindowConfig *config, config->maximized_vertically = vertically; } +MetaTileMode +meta_window_config_get_tile_mode (MetaWindowConfig *config) +{ + return config->tile_mode; +} + +int +meta_window_config_get_tile_monitor_number (MetaWindowConfig *config) +{ + return config->tile_monitor_number; +} + +double +meta_window_config_get_tile_hfraction (MetaWindowConfig *config) +{ + return config->tile_hfraction; +} + +MetaWindow * +meta_window_config_get_tile_match (MetaWindowConfig *config) +{ + return config->tile_match; +} + +void +meta_window_config_set_tile_mode (MetaWindowConfig *config, + MetaTileMode tile_mode) +{ + config->tile_mode = tile_mode; +} + +void +meta_window_config_set_tile_monitor_number (MetaWindowConfig *config, + int tile_monitor_number) +{ + config->tile_monitor_number = tile_monitor_number; +} + +void +meta_window_config_set_tile_hfraction (MetaWindowConfig *config, + double hfraction) +{ + config->tile_hfraction = hfraction; +} + +void +meta_window_config_set_tile_match (MetaWindowConfig *config, + MetaWindow *tile_match) +{ + config->tile_match = tile_match; +} + MetaWindowConfig * meta_window_config_new (void) { diff --git a/src/core/window-private.h b/src/core/window-private.h index d5e5f292ee5..e23e5a467aa 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -33,6 +33,7 @@ #include "backends/meta-logical-monitor.h" #include "clutter/clutter.h" +#include "core/meta-window-config-private.h" #include "core/stack.h" #include "meta/meta-window-config.h" #include "meta/compositor.h" @@ -259,11 +260,6 @@ struct _MetaWindow /* Initial timestamp property */ guint32 initial_timestamp; - /* The current tile mode */ - MetaTileMode tile_mode; - - int tile_monitor_number; - struct { MetaEdgeConstraint top; MetaEdgeConstraint right; @@ -271,8 +267,6 @@ struct _MetaWindow MetaEdgeConstraint left; } edge_constraints; - double tile_hfraction; - MetaLogicalMonitorId *preferred_logical_monitor; /* Area to cover when in fullscreen mode. If _NET_WM_FULLSCREEN_MONITORS has @@ -360,9 +354,6 @@ struct _MetaWindow /* Focused window that is (directly or indirectly) attached to this one */ MetaWindow *attached_focus_window; - /* The currently complementary tiled window, if any */ - MetaWindow *tile_match; - struct { MetaPlacementRule *rule; MetaPlacementState state; diff --git a/src/core/window.c b/src/core/window.c index 85e4015ee52..dd4329e4330 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -1123,9 +1123,6 @@ meta_window_constructed (GObject *object) window->require_titlebar_visible = TRUE; window->on_all_workspaces = FALSE; window->on_all_workspaces_requested = FALSE; - window->tile_mode = META_TILE_NONE; - window->tile_monitor_number = -1; - window->tile_hfraction = -1.; window->initially_iconic = FALSE; window->minimized = FALSE; window->iconic = FALSE; @@ -1212,8 +1209,6 @@ meta_window_constructed (GObject *object) meta_logical_monitor_dup_id (window->monitor); } - window->tile_match = NULL; - /* Assign this #MetaWindow a sequence number which can be used * for sorting. */ @@ -2899,14 +2894,14 @@ meta_window_maximize (MetaWindow *window, return; } - if (window->tile_mode != META_TILE_NONE) + if (meta_window_config_get_tile_mode (window->config) != META_TILE_NONE) { saved_rect = &window->saved_rect; meta_window_config_set_maximized_directions (window->config, was_maximized_horizontally, FALSE); - window->tile_mode = META_TILE_NONE; + meta_window_config_set_tile_mode (window->config, META_TILE_NONE); } meta_window_maximize_internal (window, @@ -3053,6 +3048,8 @@ meta_window_get_tile_fraction (MetaWindow *window, MetaTileMode tile_mode, double *fraction) { + double tile_hfraction = + meta_window_config_get_tile_hfraction (window->config); MetaWindow *tile_match; /* Make sure the tile match is up-to-date and matches the @@ -3065,13 +3062,13 @@ meta_window_get_tile_fraction (MetaWindow *window, else if (tile_mode == META_TILE_MAXIMIZED) *fraction = 1.; else if (tile_match) - *fraction = 1. - tile_match->tile_hfraction; + *fraction = 1. - meta_window_config_get_tile_hfraction (tile_match->config); else if (meta_window_is_tiled_side_by_side (window)) { - if (window->tile_mode != tile_mode) - *fraction = 1. - window->tile_hfraction; + if (meta_window_config_get_tile_mode (window->config) != tile_mode) + *fraction = 1. - tile_hfraction; else - *fraction = window->tile_hfraction; + *fraction = tile_hfraction; } else *fraction = .5; @@ -3082,17 +3079,21 @@ meta_window_update_tile_fraction (MetaWindow *window, int new_w, int new_h) { - MetaWindow *tile_match = window->tile_match; + MetaWindow *tile_match = meta_window_config_get_tile_match (window->config); + int tile_monitor_number; MtkRectangle work_area; MetaWindowDrag *window_drag; if (!meta_window_is_tiled_side_by_side (window)) return; + tile_monitor_number = + meta_window_config_get_tile_monitor_number (window->config); meta_window_get_work_area_for_monitor (window, - window->tile_monitor_number, + tile_monitor_number, &work_area); - window->tile_hfraction = (double)new_w / work_area.width; + meta_window_config_set_tile_hfraction (window->config, + (double) new_w / work_area.width); window_drag = meta_compositor_get_current_window_drag (window->display->compositor); @@ -3100,13 +3101,18 @@ meta_window_update_tile_fraction (MetaWindow *window, if (tile_match && window_drag && meta_window_drag_get_window (window_drag) == window) - meta_window_tile (tile_match, tile_match->tile_mode); + { + MetaTileMode tile_match_tile_mode = + meta_window_config_get_tile_mode (tile_match->config); + + meta_window_tile (tile_match, tile_match_tile_mode); + } } static void update_edge_constraints (MetaWindow *window) { - switch (window->tile_mode) + switch (meta_window_config_get_tile_mode (window->config)) { case META_TILE_NONE: window->edge_constraints.top = META_EDGE_CONSTRAINT_NONE; @@ -3125,7 +3131,7 @@ update_edge_constraints (MetaWindow *window) case META_TILE_LEFT: window->edge_constraints.top = META_EDGE_CONSTRAINT_MONITOR; - if (window->tile_match) + if (meta_window_config_get_tile_match (window->config)) window->edge_constraints.right = META_EDGE_CONSTRAINT_WINDOW; else window->edge_constraints.right = META_EDGE_CONSTRAINT_NONE; @@ -3139,7 +3145,7 @@ update_edge_constraints (MetaWindow *window) window->edge_constraints.right = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.bottom = META_EDGE_CONSTRAINT_MONITOR; - if (window->tile_match) + if (meta_window_config_get_tile_match (window->config)) window->edge_constraints.left = META_EDGE_CONSTRAINT_WINDOW; else window->edge_constraints.left = META_EDGE_CONSTRAINT_NONE; @@ -3167,34 +3173,40 @@ meta_window_is_tiled_side_by_side (MetaWindow *window) return (meta_window_config_is_maximized_vertically (config) && !meta_window_config_is_maximized_horizontally (config) && - window->tile_mode != META_TILE_NONE); + meta_window_config_get_tile_mode (config) != META_TILE_NONE); } gboolean meta_window_is_tiled_left (MetaWindow *window) { - return window->tile_mode == META_TILE_LEFT && + return meta_window_config_get_tile_mode (window->config) == META_TILE_LEFT && meta_window_is_tiled_side_by_side (window); } gboolean meta_window_is_tiled_right (MetaWindow *window) { - return window->tile_mode == META_TILE_RIGHT && + return meta_window_config_get_tile_mode (window->config) == META_TILE_RIGHT && meta_window_is_tiled_side_by_side (window); } void meta_window_untile (MetaWindow *window) { + int tile_monitor_number; + MetaTileMode tile_mode; + g_return_if_fail (META_IS_WINDOW (window)); - window->tile_monitor_number = - window->saved_maximize ? window->monitor->number - : -1; - window->tile_mode = + tile_monitor_number = window->saved_maximize ? window->monitor->number + : -1; + meta_window_config_set_tile_monitor_number (window->config, + tile_monitor_number); + + tile_mode = window->saved_maximize ? META_TILE_MAXIMIZED : META_TILE_NONE; + meta_window_config_set_tile_mode (window->config, tile_mode); if (window->saved_maximize) meta_window_maximize (window, META_MAXIMIZE_BOTH); @@ -3208,24 +3220,28 @@ meta_window_tile (MetaWindow *window, { MetaMaximizeFlags directions; MetaWindowDrag *window_drag; + MetaWindow *tile_match; + double tile_hfraction; g_return_if_fail (META_IS_WINDOW (window)); - meta_window_get_tile_fraction (window, tile_mode, &window->tile_hfraction); - window->tile_mode = tile_mode; + meta_window_get_tile_fraction (window, tile_mode, &tile_hfraction); + meta_window_config_set_tile_hfraction (window->config, tile_hfraction); + meta_window_config_set_tile_mode (window->config, tile_mode); /* Don't do anything if no tiling is requested */ - if (window->tile_mode == META_TILE_NONE) + if (tile_mode == META_TILE_NONE) { - window->tile_monitor_number = -1; + meta_window_config_set_tile_monitor_number (window->config, -1); return; } - else if (window->tile_monitor_number < 0) + else if (meta_window_config_get_tile_monitor_number (window->config) < 0) { - window->tile_monitor_number = window->monitor->number; + meta_window_config_set_tile_monitor_number (window->config, + window->monitor->number); } - if (window->tile_mode == META_TILE_MAXIMIZED) + if (tile_mode == META_TILE_MAXIMIZED) directions = META_MAXIMIZE_BOTH; else directions = META_MAXIMIZE_VERTICAL; @@ -3235,9 +3251,10 @@ meta_window_tile (MetaWindow *window, window_drag = meta_compositor_get_current_window_drag (window->display->compositor); - if (!window->tile_match || + tile_match = meta_window_config_get_tile_match (window->config); + if (!tile_match || !window_drag || - window->tile_match != meta_window_drag_get_window (window_drag)) + tile_match != meta_window_drag_get_window (window_drag)) { MtkRectangle old_frame_rect, old_buffer_rect; @@ -3379,7 +3396,7 @@ meta_window_unmaximize (MetaWindow *window, meta_window_get_buffer_rect (window, &old_buffer_rect); if (unmaximize_vertically) - window->tile_mode = META_TILE_NONE; + meta_window_config_set_tile_mode (window->config, META_TILE_NONE); meta_topic (META_DEBUG_WINDOW_OPS, "Unmaximizing %s%s", @@ -3950,12 +3967,17 @@ meta_window_update_for_monitors_changed (MetaWindow *window) old = window->monitor; new = meta_window_find_monitor_from_id (window); - if (window->tile_mode != META_TILE_NONE) + if (meta_window_config_get_tile_mode (window->config) != META_TILE_NONE) { + int new_monitor_number; + if (new) - window->tile_monitor_number = new->number; + new_monitor_number = new->number; else - window->tile_monitor_number = -1; + new_monitor_number = -1; + + meta_window_config_set_tile_monitor_number (window->config, + new_monitor_number); } if (new && old) @@ -4389,8 +4411,8 @@ meta_window_move_to_monitor (MetaWindow *window, { MtkRectangle old_area, new_area; - if (window->tile_mode != META_TILE_NONE) - window->tile_monitor_number = monitor; + if (meta_window_config_get_tile_mode (window->config) != META_TILE_NONE) + meta_window_config_set_tile_monitor_number (window->config, monitor); meta_window_get_work_area_for_monitor (window, window->monitor->number, @@ -4437,12 +4459,15 @@ adjust_size_for_tile_match (MetaWindow *window, int *new_h) { MtkRectangle work_area, rect; - MetaWindow *tile_match = window->tile_match; + MetaWindow *tile_match = meta_window_config_get_tile_match (window->config); + int tile_monitor_number; if (!meta_window_is_tiled_side_by_side (window) || !tile_match) return; - meta_window_get_work_area_for_monitor (window, window->tile_monitor_number, + tile_monitor_number = + meta_window_config_get_tile_monitor_number (window->config); + meta_window_get_work_area_for_monitor (window, tile_monitor_number, &work_area); /* Make sure the resize does not break minimum sizes */ @@ -6027,7 +6052,8 @@ meta_window_get_work_area_all_monitors (MetaWindow *window, int meta_window_get_current_tile_monitor_number (MetaWindow *window) { - int tile_monitor_number = window->tile_monitor_number; + int tile_monitor_number = + meta_window_config_get_tile_monitor_number (window->config); if (tile_monitor_number < 0) { @@ -7163,20 +7189,26 @@ meta_window_has_modals (MetaWindow *window) MetaWindow * meta_window_get_tile_match (MetaWindow *window) { - return window->tile_match; + return meta_window_config_get_tile_match (window->config); } void meta_window_compute_tile_match (MetaWindow *window) { - window->tile_match = meta_window_find_tile_match (window, window->tile_mode); + MetaTileMode tile_mode = meta_window_config_get_tile_mode (window->config); + MetaWindow *tile_match; + + tile_match = meta_window_find_tile_match (window, tile_mode); + meta_window_config_set_tile_match (window->config, tile_match); } static MetaWindow * meta_window_find_tile_match (MetaWindow *window, MetaTileMode current_mode) { - MetaWindow *match; + int tile_monitor_number; + MetaWindow *other_window; + MetaWindow *match = NULL; MetaStack *stack; MetaTileMode match_tile_mode = META_TILE_NONE; @@ -7192,15 +7224,34 @@ meta_window_find_tile_match (MetaWindow *window, stack = window->display->stack; - for (match = meta_stack_get_top (stack); - match; - match = meta_stack_get_below (stack, match, FALSE)) + tile_monitor_number = + meta_window_config_get_tile_monitor_number (window->config); + + for (other_window = meta_stack_get_top (stack); + other_window; + other_window = meta_stack_get_below (stack, other_window, FALSE)) { - if (!match->minimized && - match->tile_mode == match_tile_mode && - match->tile_monitor_number == window->tile_monitor_number && - meta_window_get_workspace (match) == meta_window_get_workspace (window)) - break; + MetaTileMode other_tile_mode; + int other_tile_monitor_number; + + if (other_window->minimized) + continue; + + other_tile_mode = meta_window_config_get_tile_mode (other_window->config); + if (other_tile_mode != match_tile_mode) + continue; + + other_tile_monitor_number = + meta_window_config_get_tile_monitor_number (other_window->config); + + if (other_tile_monitor_number != tile_monitor_number) + continue; + + if (meta_window_get_workspace (other_window) == meta_window_get_workspace (window)) + { + match = other_window; + break; + } } if (match) @@ -7231,11 +7282,11 @@ meta_window_find_tile_match (MetaWindow *window, * rather than a match for a potential tile mode, then discard * windows with too much gap or overlap */ - if (window->tile_mode == current_mode && + if (meta_window_config_get_tile_mode (window->config) == current_mode && !(window_drag && meta_grab_op_is_resizing (meta_window_drag_get_grab_op (window_drag)) && meta_window_drag_get_window (window_drag) == window && - window->tile_match != NULL)) + meta_window_config_get_tile_match (window->config))) { int threshold = meta_prefs_get_drag_threshold (); if (ABS (topmost_rect.x - bottommost_rect.x - bottommost_rect.width) > threshold && diff --git a/src/wayland/meta-wayland-gtk-shell.c b/src/wayland/meta-wayland-gtk-shell.c index 8a82d3507a8..992839a6ab1 100644 --- a/src/wayland/meta-wayland-gtk-shell.c +++ b/src/wayland/meta-wayland-gtk-shell.c @@ -395,13 +395,14 @@ fill_states (struct wl_array *states, MetaWindow *window, struct wl_resource *resource) { + MetaTileMode tile_mode = meta_window_config_get_tile_mode (window->config); int version; version = wl_resource_get_version (resource); if (version < GTK_SURFACE1_CONFIGURE_EDGES_SINCE_VERSION && - (window->tile_mode == META_TILE_LEFT || - window->tile_mode == META_TILE_RIGHT)) + (tile_mode == META_TILE_LEFT || + tile_mode == META_TILE_RIGHT)) add_state_value (states, GTK_SURFACE1_STATE_TILED); if (version >= GTK_SURFACE1_STATE_TILED_TOP_SINCE_VERSION && diff --git a/src/wayland/meta-wayland-xdg-session-state.c b/src/wayland/meta-wayland-xdg-session-state.c index f63923d3029..ccde2beec89 100644 --- a/src/wayland/meta-wayland-xdg-session-state.c +++ b/src/wayland/meta-wayland-xdg-session-state.c @@ -321,6 +321,7 @@ meta_wayland_xdg_session_state_save_window (MetaSessionState *state, META_WAYLAND_XDG_SESSION_STATE (state); MetaWaylandXdgToplevelState *toplevel_state; MtkRectangle rect; + MetaTileMode tile_mode; toplevel_state = meta_wayland_xdg_session_state_ensure_toplevel (xdg_session_state, @@ -331,6 +332,8 @@ meta_wayland_xdg_session_state_save_window (MetaSessionState *state, "minimized", &toplevel_state->is_minimized, NULL); + tile_mode = meta_window_config_get_tile_mode (window->config); + if (meta_window_get_maximized (window) == (META_MAXIMIZE_VERTICAL | META_MAXIMIZE_HORIZONTAL)) { @@ -338,12 +341,12 @@ meta_wayland_xdg_session_state_save_window (MetaSessionState *state, toplevel_state->tiled.rect = rect; } - else if (window->tile_mode == META_TILE_LEFT || - window->tile_mode == META_TILE_RIGHT) + else if (tile_mode == META_TILE_LEFT || + tile_mode == META_TILE_RIGHT) { - if (window->tile_mode == META_TILE_LEFT) + if (tile_mode == META_TILE_LEFT) toplevel_state->window_state = WINDOW_STATE_TILED_LEFT; - else if (window->tile_mode == META_TILE_RIGHT) + else if (tile_mode == META_TILE_RIGHT) toplevel_state->window_state = WINDOW_STATE_TILED_RIGHT; toplevel_state->tiled.rect = rect; @@ -413,7 +416,10 @@ meta_wayland_xdg_session_state_restore_window (MetaSessionState *state, rect = &toplevel_state->tiled.rect; target_monitor = determine_monitor_for_rect (window, rect); if (target_monitor) - window->tile_monitor_number = target_monitor->number; + { + meta_window_config_set_tile_monitor_number (window->config, + target_monitor->number); + } break; } diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c index 10e82e6b9fe..36a2f00bad2 100644 --- a/src/wayland/meta-window-wayland.c +++ b/src/wayland/meta-window-wayland.c @@ -265,6 +265,7 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, gboolean can_move_now = FALSE; MtkRectangle configured_rect; MtkRectangle frame_rect; + MetaTileMode tile_mode; MetaGravity gravity; int geometry_scale; int new_x; @@ -287,6 +288,7 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, * to the Wayland surface. */ geometry_scale = meta_window_wayland_get_geometry_scale (window); frame_rect = meta_window_config_get_rect (window->config); + tile_mode = meta_window_config_get_tile_mode (window->config); configured_rect.width = constrained_rect.width; configured_rect.height = constrained_rect.height; @@ -405,7 +407,7 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, if (!meta_wayland_surface_get_buffer (wl_window->surface) && !meta_window_is_maximized (window) && - window->tile_mode == META_TILE_NONE && + tile_mode == META_TILE_NONE && !meta_window_is_fullscreen (window)) return; -- GitLab From 2f6763cb24b66c94dbc75e88f98f9697ce0d598c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 17 Apr 2025 22:55:14 +0200 Subject: [PATCH 04/38] tests/test-runner: Add add/substract support to math routine Part-of: --- src/tests/test-runner.c | 46 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/tests/test-runner.c b/src/tests/test-runner.c index 24e7788985a..caa60def0de 100644 --- a/src/tests/test-runner.c +++ b/src/tests/test-runner.c @@ -510,6 +510,46 @@ maybe_divide (const char *str, return value; } +static int +maybe_add (const char *str, + int value, + const char **out_str) +{ + *out_str = str; + + if (str[0] == '+') + { + double term; + + str += 1; + term = g_strtod (str, (char **) out_str); + + value = (int) round (value + term); + } + + return value; +} + +static int +maybe_subtract (const char *str, + int value, + const char **out_str) +{ + *out_str = str; + + if (str[0] == '-') + { + double term; + + str += 1; + term = g_strtod (str, (char **) out_str); + + value = (int) round (value - term); + } + + return value; +} + static int maybe_do_math (const char *str, int value, @@ -523,6 +563,12 @@ maybe_do_math (const char *str, case '/': value = maybe_divide (str, value, &str); break; + case '+': + value = maybe_add (str, value, &str); + break; + case '-': + value = maybe_subtract (str, value, &str); + break; default: *out_str = str; return value; -- GitLab From 1daeff71f7acccd0f79c36e21707567a2449b0f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 17 Apr 2025 22:55:42 +0200 Subject: [PATCH 05/38] tests/test-runner: Sort includes Part-of: --- src/tests/test-runner.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/test-runner.c b/src/tests/test-runner.c index caa60def0de..2ea3b6010d9 100644 --- a/src/tests/test-runner.c +++ b/src/tests/test-runner.c @@ -27,12 +27,12 @@ #include "backends/meta-virtual-monitor.h" #include "compositor/meta-window-actor-private.h" #include "core/meta-workspace-manager-private.h" +#include "core/meta-workspace-manager-private.h" #include "core/window-private.h" +#include "core/workspace-private.h" #include "meta-test/meta-context-test.h" #include "meta/util.h" #include "meta/window.h" -#include "core/meta-workspace-manager-private.h" -#include "core/workspace-private.h" #include "tests/meta-test-utils.h" #include "wayland/meta-wayland.h" #include "wayland/meta-window-wayland.h" -- GitLab From ce62f13081f1101eef53941605dd3299c08c14fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 17 Apr 2025 22:56:02 +0200 Subject: [PATCH 06/38] tests/test-runner: Add edge resize commands This emulates an interactive resize done from an edge, e.g. xdg_toplevel.resize(). It has different phases, just like a drag grab - begin, update and end, where multiple updates can happen within each sequence. Part-of: --- src/compositor/compositor-private.h | 1 + src/compositor/meta-window-drag.h | 3 + src/tests/test-runner.c | 106 ++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h index 75dcf48fb9d..73975d20933 100644 --- a/src/compositor/compositor-private.h +++ b/src/compositor/compositor-private.h @@ -107,6 +107,7 @@ gboolean meta_compositor_drag_window (MetaCompositor *compositor, graphene_point_t *pos_hint, ClutterActor *grab_actor); +META_EXPORT_TEST MetaWindowDrag * meta_compositor_get_current_window_drag (MetaCompositor *compositor); void meta_compositor_grab_begin (MetaCompositor *compositor); diff --git a/src/compositor/meta-window-drag.h b/src/compositor/meta-window-drag.h index c6b51f80816..e5f5c316910 100644 --- a/src/compositor/meta-window-drag.h +++ b/src/compositor/meta-window-drag.h @@ -19,6 +19,7 @@ #pragma once +#include "core/util-private.h" #include "meta/common.h" #include "meta/window.h" @@ -35,10 +36,12 @@ gboolean meta_window_drag_begin (MetaWindowDrag *drag, uint32_t timestamp, ClutterActor *grab_actor); +META_EXPORT_TEST void meta_window_drag_end (MetaWindowDrag *drag); void meta_window_drag_update_resize (MetaWindowDrag *drag); +META_EXPORT_TEST MetaWindow * meta_window_drag_get_window (MetaWindowDrag *window_drag); MetaGrabOp meta_window_drag_get_grab_op (MetaWindowDrag *window_drag); diff --git a/src/tests/test-runner.c b/src/tests/test-runner.c index 2ea3b6010d9..ea9262948f2 100644 --- a/src/tests/test-runner.c +++ b/src/tests/test-runner.c @@ -25,7 +25,10 @@ #include #include "backends/meta-virtual-monitor.h" +#include "clutter/clutter.h" +#include "compositor/compositor-private.h" #include "compositor/meta-window-actor-private.h" +#include "compositor/meta-window-drag.h" #include "core/meta-workspace-manager-private.h" #include "core/meta-workspace-manager-private.h" #include "core/window-private.h" @@ -901,6 +904,23 @@ test_case_parse_signal (TestCase *test, return TRUE; } +static MetaGrabOp +grab_op_from_edge (const char *edge) +{ + MetaGrabOp op = META_GRAB_OP_WINDOW_BASE; + + if (strcmp (edge, "top") == 0) + op |= META_GRAB_OP_WINDOW_DIR_NORTH; + else if (strcmp (edge, "bottom") == 0) + op |= META_GRAB_OP_WINDOW_DIR_SOUTH; + else if (strcmp (edge, "left") == 0) + op |= META_GRAB_OP_WINDOW_DIR_WEST; + else if (strcmp (edge, "right") == 0) + op |= META_GRAB_OP_WINDOW_DIR_EAST; + + return op; +} + static gboolean test_case_do (TestCase *test, const char *filename, @@ -1104,6 +1124,92 @@ test_case_do (TestCase *test, argv[2], argv[3], NULL)) return FALSE; } + else if (strcmp (argv[0], "begin_resize") == 0) + { + MetaBackend *backend = meta_context_get_backend (test->context); + ClutterSeat *seat = meta_backend_get_default_seat (backend); + ClutterInputDevice *pointer = clutter_seat_get_pointer (seat); + MetaTestClient *client; + const char *window_id; + MetaWindow *window; + MetaGrabOp grab_op; + MtkRectangle rect; + gboolean ret; + MetaWindowDrag *window_drag; + + if (argc != 3) + BAD_COMMAND("usage: %s / [top|bottom|left|right]", argv[0]); + + if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error)) + return FALSE; + + window = meta_test_client_find_window (client, window_id, error); + + grab_op = grab_op_from_edge (argv[2]); + + meta_window_get_frame_rect (window, &rect); + graphene_point_t grab_origin; + + grab_origin = GRAPHENE_POINT_INIT (rect.x + rect.width / 2.0f, + rect.y + rect.height / 2.0f); + + window_drag = + meta_compositor_get_current_window_drag (window->display->compositor); + g_assert_null (window_drag); + ret = meta_window_begin_grab_op (window, + grab_op, + pointer, NULL, + meta_display_get_current_time_roundtrip (window->display), + &grab_origin); + g_assert_true (ret); + } + else if (strcmp (argv[0], "update_resize") == 0) + { + MetaTestClient *client; + const char *window_id; + MetaWindow *window; + MtkRectangle rect; + int delta_x, delta_y; + + if (argc != 4) + BAD_COMMAND("usage: %s / ", argv[0]); + + if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error)) + return FALSE; + + window = meta_test_client_find_window (client, window_id, error); + + meta_window_get_frame_rect (window, &rect); + delta_x = atoi (argv[2]); + delta_y = atoi (argv[3]); + + meta_window_resize_frame (window, + TRUE, + rect.width + delta_x, + rect.height + delta_y); + } + else if (strcmp (argv[0], "end_resize") == 0) + { + MetaTestClient *client; + const char *window_id; + MetaWindow *window; + MetaWindowDrag *window_drag; + + if (argc != 2) + BAD_COMMAND("usage: %s /", argv[0]); + + if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error)) + return FALSE; + + window = meta_test_client_find_window (client, window_id, error); + + window_drag = + meta_compositor_get_current_window_drag (window->display->compositor); + g_assert_nonnull (window_drag); + g_assert_true (meta_window_drag_get_window (window_drag) == window); + + meta_window_drag_end (window_drag); + } else if (strcmp (argv[0], "move") == 0) { MetaWindow *window; -- GitLab From 8910b055f9e87224956f7d8790bdf7cada8b1873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 28 Apr 2025 13:54:04 +0200 Subject: [PATCH 07/38] tests/test-runner: Add 'x11_geometry' command This aims to allow testing of X11 clients started with the typical WIDTHxHEIGHT+X+Y geometry, which sets an initial position with the 'USPosition' (user specified x,y) flag set. Part-of: --- src/tests/test-client.c | 12 ++++++++++++ src/tests/test-runner.c | 15 +++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/tests/test-client.c b/src/tests/test-client.c index 7aa3b9306fd..a4b15311397 100644 --- a/src/tests/test-client.c +++ b/src/tests/test-client.c @@ -611,6 +611,18 @@ process_line (const char *line) width, height - titlebar_height); } + else if (strcmp (argv[0], "x11_geometry") == 0) + { + GtkWidget *window; + + window = lookup_window (argv[1]); + if (!window) + goto out; + + G_GNUC_BEGIN_IGNORE_DEPRECATIONS + gtk_window_parse_geometry (GTK_WINDOW (window), argv[2]); + G_GNUC_END_IGNORE_DEPRECATIONS + } else if (strcmp (argv[0], "raise") == 0) { if (argc != 2) diff --git a/src/tests/test-runner.c b/src/tests/test-runner.c index ea9262948f2..bbc6ff08525 100644 --- a/src/tests/test-runner.c +++ b/src/tests/test-runner.c @@ -1124,6 +1124,21 @@ test_case_do (TestCase *test, argv[2], argv[3], NULL)) return FALSE; } + else if (strcmp (argv[0], "x11_geometry") == 0) + { + MetaTestClient *client; + const char *window_id; + + if (argc != 3) + BAD_COMMAND ("usage: %s / ", argv[0]); + + if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error)) + return FALSE; + + if (!meta_test_client_do (client, error, argv[0], window_id, + argv[2], NULL)) + return FALSE; + } else if (strcmp (argv[0], "begin_resize") == 0) { MetaBackend *backend = meta_context_get_backend (test->context); -- GitLab From 99799c9e1a56b2eb200bbc27f8bab0e4977a4f6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 28 Apr 2025 14:03:46 +0200 Subject: [PATCH 08/38] tests: Add X11 geometry test case This places a window using a WIDTHxHEIGHT+X+Y string, and ensures it was placed accordingly. The size isn't checked, because currently the compositor side size includes the window decorations, which has an unknown height, making it annoying to test. Part-of: --- src/tests/meson.build | 1 + src/tests/stacking/x11-move.metatest | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 src/tests/stacking/x11-move.metatest diff --git a/src/tests/meson.build b/src/tests/meson.build index d342b20d080..60d63eb8310 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -966,6 +966,7 @@ stacking_tests = [ 'sticky-transients', 'strut-monitor-changes', 'window-placement', + 'x11-move', ] foreach stacking_test: stacking_tests diff --git a/src/tests/stacking/x11-move.metatest b/src/tests/stacking/x11-move.metatest new file mode 100644 index 00000000000..552298be6a4 --- /dev/null +++ b/src/tests/stacking/x11-move.metatest @@ -0,0 +1,9 @@ +resize_monitor default 640 480 +add_monitor secondary 800 600 + +new_client x x11 +create x/1 +x11_geometry x/1 100x100+700+10 +show x/1 + +assert_position x/1 700 10 -- GitLab From ce8e8fb6a209db12662669763de87b2254ac5145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 23 Apr 2025 13:42:45 +0200 Subject: [PATCH 09/38] tests/test-runner: Use g_auto* when parsing lines Part-of: --- src/tests/test-runner.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/tests/test-runner.c b/src/tests/test-runner.c index bbc6ff08525..6ed4aa56393 100644 --- a/src/tests/test-runner.c +++ b/src/tests/test-runner.c @@ -2371,14 +2371,16 @@ run_test (MetaContext *context, int line_no = 0; while (error == NULL) { - char *line = g_data_input_stream_read_line_utf8 (in, NULL, NULL, &error); + g_autofree char *line = NULL; + int argc; + g_auto (GStrv) argv = NULL; + + line = g_data_input_stream_read_line_utf8 (in, NULL, NULL, &error); if (line == NULL) break; line_no++; - int argc; - char **argv = NULL; if (!g_shell_parse_argv (line, &argc, &argv, &error)) { if (g_error_matches (error, G_SHELL_ERROR, G_SHELL_ERROR_EMPTY_STRING)) @@ -2395,9 +2397,6 @@ run_test (MetaContext *context, next: if (error) g_prefix_error (&error, "%d: ", line_no); - - g_free (line); - g_strfreev (argv); } { -- GitLab From 34e95fa63117eaabc86de5e5eec2d93758cf20b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 23 Apr 2025 13:44:20 +0200 Subject: [PATCH 10/38] tests/test-client: Add --dont-exit-on-eof This is useful when one wants to use the test client to quickly experiment to reproduce some issue, e.g.: $ cat test create w csd maximize w show w $ ./build/src/tests/mutter-test-client --wayland --dont-exit-on-eof < test ... will show a maximized window but won't exit until pressing ^C The same is possible with gdb: $ gdb ./build/src/tests/mutter-test-client (gdb) run --wayland --dont-exit-on-eof < test ... Part-of: --- src/tests/test-client.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/tests/test-client.c b/src/tests/test-client.c index a4b15311397..05bece80082 100644 --- a/src/tests/test-client.c +++ b/src/tests/test-client.c @@ -33,6 +33,7 @@ const char *client_id = "0"; static gboolean wayland; +static gboolean dont_exit_on_eof; GHashTable *windows; GQuark event_source_quark; GQuark event_handlers_quark; @@ -1031,7 +1032,8 @@ on_line_received (GObject *source, { if (error != NULL) g_printerr ("Error reading from stdin: %s\n", error->message); - gtk_main_quit (); + if (!dont_exit_on_eof) + gtk_main_quit (); return; } @@ -1057,7 +1059,8 @@ read_next_line (GDataInputStream *in) { if (error) g_printerr ("Error reading from stdin: %s\n", error->message); - gtk_main_quit (); + if (!dont_exit_on_eof) + gtk_main_quit (); return; } @@ -1078,6 +1081,12 @@ const GOptionEntry options[] = { "Create a wayland client, not an X11 one", NULL }, + { + "dont-exit-on-eof", 0, 0, G_OPTION_ARG_NONE, + &dont_exit_on_eof, + "Don't terminate client when reaching end of file", + NULL + }, { "client-id", 0, 0, G_OPTION_ARG_STRING, &client_id, -- GitLab From a47ddbd31e1ae6d26dc0664114d3a998b2fc01bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 25 Apr 2025 22:37:12 +0200 Subject: [PATCH 11/38] tests/wayland: Let session management tests share the same base path This means one can run all session management tests by passing `-p` and the shared path. Part-of: --- src/tests/wayland-unit-tests.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/tests/wayland-unit-tests.c b/src/tests/wayland-unit-tests.c index 8d8842c32dd..db5b7ac7444 100644 --- a/src/tests/wayland-unit-tests.c +++ b/src/tests/wayland-unit-tests.c @@ -627,7 +627,7 @@ toplevel_reuse_surface (void) } static void -toplevel_sessions (void) +toplevel_sessions_basic (void) { MetaWaylandTestClient *wayland_test_client; @@ -1554,11 +1554,11 @@ init_tests (void) toplevel_apply_limits); g_test_add_func ("/wayland/toplevel/activation", toplevel_activation); - g_test_add_func ("/wayland/toplevel/sessions", - toplevel_sessions); - g_test_add_func ("/wayland/toplevel/sessions-replace", + g_test_add_func ("/wayland/toplevel/sessions/basic", + toplevel_sessions_basic); + g_test_add_func ("/wayland/toplevel/sessions/replace", toplevel_sessions_replace); - g_test_add_func ("/wayland/toplevel/sessions-restore", + g_test_add_func ("/wayland/toplevel/sessions/restore", toplevel_sessions_restore); #ifdef MUTTER_PRIVILEGED_TEST (void)(toplevel_sessions_restore_maximized); @@ -1568,13 +1568,13 @@ init_tests (void) (void)(toplevel_bounds_struts); (void)(toplevel_bounds_monitors); #else - g_test_add_func ("/wayland/toplevel/sessions-restore-maximized", + g_test_add_func ("/wayland/toplevel/sessions/restore-maximized", toplevel_sessions_restore_maximized); - g_test_add_func ("/wayland/toplevel/sessions-restore-tiled", + g_test_add_func ("/wayland/toplevel/sessions/restore-tiled", toplevel_sessions_restore_tiled); - g_test_add_func ("/wayland/toplevel/sessions-restore-fullscreen", + g_test_add_func ("/wayland/toplevel/sessions/restore-fullscreen", toplevel_sessions_restore_fullscreen); - g_test_add_func ("/wayland/toplevel/sessions-restore-fullscreen-monitor-removed", + g_test_add_func ("/wayland/toplevel/sessions/restore-fullscreen-monitor-removed", toplevel_sessions_restore_fullscreen_monitor_removed); g_test_add_func ("/wayland/toplevel/bounds/struts", toplevel_bounds_struts); -- GitLab From 14248a194adfd20a52e636bd8487386a1402b561 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 22 Apr 2025 21:40:31 +0200 Subject: [PATCH 12/38] window: Rename update_layout() to idle_move_resize() It's the function called when doing queue(MOVE_RESIZE) so name it accordingly. Part-of: --- src/core/display.c | 2 +- src/core/window-private.h | 2 +- src/core/window.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/display.c b/src/core/display.c index b7731197edc..4cadf007c69 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -3548,7 +3548,7 @@ static void move_resize (MetaDisplay *display, GList *windows) { - g_list_foreach (windows, (GFunc) meta_window_update_layout, NULL); + g_list_foreach (windows, (GFunc) meta_window_idle_move_resize, NULL); g_list_foreach (windows, (GFunc) warn_on_incorrectly_unmanaged_window, NULL); } diff --git a/src/core/window-private.h b/src/core/window-private.h index e23e5a467aa..c780c774a09 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -824,7 +824,7 @@ void meta_window_update_visibility (MetaWindow *window); void meta_window_clear_queued (MetaWindow *window); -void meta_window_update_layout (MetaWindow *window); +void meta_window_idle_move_resize (MetaWindow *window); gboolean meta_window_calculate_bounds (MetaWindow *window, int *bounds_width, diff --git a/src/core/window.c b/src/core/window.c index dd4329e4330..0e8da9deaba 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -4172,7 +4172,7 @@ meta_window_move_resize_internal (MetaWindow *window, } /* If we did placement, then we need to save the position that the window - * was placed at to make sure that meta_window_update_layout() places the + * was placed at to make sure that meta_window_idle_move_resize() places the * window correctly. */ if (did_placement) @@ -4523,7 +4523,7 @@ meta_window_resize_frame (MetaWindow *window, } void -meta_window_update_layout (MetaWindow *window) +meta_window_idle_move_resize (MetaWindow *window) { meta_window_move_resize_frame (window, FALSE, window->unconstrained_rect.x, -- GitLab From 084506d8135a8873803ea76f4d3db9f3cfaaa326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 22 Apr 2025 23:57:51 +0200 Subject: [PATCH 13/38] wayland/window: Make window configuration ref counted This makes it easier to track multiple ones. Part-of: --- src/wayland/meta-wayland-window-configuration.c | 17 +++++++++++++++-- src/wayland/meta-wayland-window-configuration.h | 9 ++++++++- src/wayland/meta-wayland-xdg-shell.c | 4 +--- src/wayland/meta-window-wayland.c | 17 +++++++++-------- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/wayland/meta-wayland-window-configuration.c b/src/wayland/meta-wayland-window-configuration.c index f92c1b34a7d..70340e63951 100644 --- a/src/wayland/meta-wayland-window-configuration.c +++ b/src/wayland/meta-wayland-window-configuration.c @@ -40,6 +40,8 @@ meta_wayland_window_configuration_new (MetaWindow *window, configuration = g_new0 (MetaWaylandWindowConfiguration, 1); *configuration = (MetaWaylandWindowConfiguration) { + .ref_count = G_REF_COUNT_INIT, + .serial = ++global_serial_counter, .bounds_width = bounds_width, @@ -85,6 +87,8 @@ meta_wayland_window_configuration_new_relative (MetaWindow *window, configuration = g_new0 (MetaWaylandWindowConfiguration, 1); *configuration = (MetaWaylandWindowConfiguration) { + .ref_count = G_REF_COUNT_INIT, + .serial = ++global_serial_counter, .has_relative_position = TRUE, @@ -111,6 +115,7 @@ meta_wayland_window_configuration_new_empty (int bounds_width, configuration = g_new0 (MetaWaylandWindowConfiguration, 1); *configuration = (MetaWaylandWindowConfiguration) { + .ref_count = G_REF_COUNT_INIT, .serial = ++global_serial_counter, .scale = scale, .bounds_width = bounds_width, @@ -120,10 +125,18 @@ meta_wayland_window_configuration_new_empty (int bounds_width, return configuration; } +MetaWaylandWindowConfiguration * +meta_wayland_window_configuration_ref (MetaWaylandWindowConfiguration *configuration) +{ + g_ref_count_inc (&configuration->ref_count); + return configuration; +} + void -meta_wayland_window_configuration_free (MetaWaylandWindowConfiguration *configuration) +meta_wayland_window_configuration_unref (MetaWaylandWindowConfiguration *configuration) { - g_free (configuration); + if (g_ref_count_dec (&configuration->ref_count)) + g_free (configuration); } MetaWindowConfig * diff --git a/src/wayland/meta-wayland-window-configuration.h b/src/wayland/meta-wayland-window-configuration.h index 762816e5ac0..e969662df33 100644 --- a/src/wayland/meta-wayland-window-configuration.h +++ b/src/wayland/meta-wayland-window-configuration.h @@ -27,6 +27,8 @@ struct _MetaWaylandWindowConfiguration { + grefcount ref_count; + uint32_t serial; gboolean has_position; @@ -72,7 +74,9 @@ MetaWaylandWindowConfiguration * meta_wayland_window_configuration_new_empty (in int bounds_height, int scale); -void meta_wayland_window_configuration_free (MetaWaylandWindowConfiguration *configuration); +MetaWaylandWindowConfiguration * meta_wayland_window_configuration_ref (MetaWaylandWindowConfiguration *configuration); + +void meta_wayland_window_configuration_unref (MetaWaylandWindowConfiguration *configuration); MetaWindowConfig * meta_window_config_new_from_wayland_window_configuration (MetaWindow *window, MetaWaylandWindowConfiguration *configuration); @@ -80,3 +84,6 @@ MetaWindowConfig * meta_window_config_new_from_wayland_window_configuration (Met MetaWaylandWindowConfiguration * meta_wayland_window_configuration_apply_window_config (MetaWindow *window, MetaWaylandWindowConfiguration *configuration, MetaWindowConfig *window_config); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaWaylandWindowConfiguration, + meta_wayland_window_configuration_unref) diff --git a/src/wayland/meta-wayland-xdg-shell.c b/src/wayland/meta-wayland-xdg-shell.c index bed32b3fa4b..2c08efb0360 100644 --- a/src/wayland/meta-wayland-xdg-shell.c +++ b/src/wayland/meta-wayland-xdg-shell.c @@ -887,7 +887,7 @@ meta_wayland_xdg_toplevel_apply_state (MetaWaylandSurfaceRole *surface_role, if (!xdg_surface_priv->configure_sent) { - MetaWaylandWindowConfiguration *configuration; + g_autoptr (MetaWaylandWindowConfiguration) configuration = NULL; g_autoptr (MetaWindowConfig) window_config = NULL; int bounds_width, bounds_height, geometry_scale; MtkRectangle rect; @@ -929,8 +929,6 @@ meta_wayland_xdg_toplevel_apply_state (MetaWaylandSurfaceRole *surface_role, window_config); 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 36a2f00bad2..5f6b34bca0b 100644 --- a/src/wayland/meta-window-wayland.c +++ b/src/wayland/meta-window-wayland.c @@ -197,14 +197,15 @@ meta_window_wayland_configure (MetaWindowWayland *wl_window, meta_wayland_surface_configure_notify (wl_window->surface, configuration); wl_window->pending_configurations = - g_list_prepend (wl_window->pending_configurations, configuration); + g_list_prepend (wl_window->pending_configurations, + meta_wayland_window_configuration_ref (configuration)); } static void surface_state_changed (MetaWindow *window) { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); - MetaWaylandWindowConfiguration *configuration; + g_autoptr (MetaWaylandWindowConfiguration) configuration = NULL; int bounds_width; int bounds_height; @@ -365,7 +366,7 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, constrained_rect.width != frame_rect.width || constrained_rect.height != frame_rect.height) { - MetaWaylandWindowConfiguration *configuration; + g_autoptr (MetaWaylandWindowConfiguration) configuration = NULL; configuration = meta_wayland_window_configuration_new_relative (window, @@ -401,7 +402,7 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, constrained_rect.height != frame_rect.height || flags & META_MOVE_RESIZE_STATE_CHANGED) { - MetaWaylandWindowConfiguration *configuration; + g_autoptr (MetaWaylandWindowConfiguration) configuration = NULL; int bounds_width; int bounds_height; @@ -926,9 +927,9 @@ meta_window_wayland_finalize (GObject *object) MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (object); g_clear_pointer (&wl_window->last_acked_configuration, - meta_wayland_window_configuration_free); + meta_wayland_window_configuration_unref); g_list_free_full (wl_window->pending_configurations, - (GDestroyNotify) meta_wayland_window_configuration_free); + (GDestroyNotify) meta_wayland_window_configuration_unref); G_OBJECT_CLASS (meta_window_wayland_parent_class)->finalize (object); } @@ -1118,7 +1119,7 @@ acquire_acked_configuration (MetaWindowWayland *wl_window, if (is_matching_configuration) tail = g_list_delete_link (tail, l); g_list_free_full (tail, - (GDestroyNotify) meta_wayland_window_configuration_free); + (GDestroyNotify) meta_wayland_window_configuration_unref); if (is_matching_configuration) return configuration; @@ -1334,7 +1335,7 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, } g_clear_pointer (&wl_window->last_acked_configuration, - meta_wayland_window_configuration_free); + meta_wayland_window_configuration_unref); wl_window->last_acked_configuration = g_steal_pointer (&acked_configuration); /* Force unconstrained move when running toplevel drags */ -- GitLab From 78c451566d299a0140fd444820e7c186f013838c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 23 Apr 2025 13:48:11 +0200 Subject: [PATCH 14/38] mtk/rectangle: Add helper to check if a rectangle is empty Returns true if either width or height is 0. Part-of: --- mtk/mtk/mtk-rectangle.c | 6 ++++++ mtk/mtk/mtk-rectangle.h | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/mtk/mtk/mtk-rectangle.c b/mtk/mtk/mtk-rectangle.c index 25a88bb6f88..63be7ab0713 100644 --- a/mtk/mtk/mtk-rectangle.c +++ b/mtk/mtk/mtk-rectangle.c @@ -445,6 +445,12 @@ mtk_rectangle_is_adjacent_to (const MtkRectangle *rect, return FALSE; } +gboolean +mtk_rectangle_is_empty (const MtkRectangle *rect) +{ + return rect->width == 0 || rect->height == 0; +} + /** * mtk_rectangle_transform: * @rect: the #MtkRectangle to be transformed diff --git a/mtk/mtk/mtk-rectangle.h b/mtk/mtk/mtk-rectangle.h index a5b641bf88a..9d5f86fcf7e 100644 --- a/mtk/mtk/mtk-rectangle.h +++ b/mtk/mtk/mtk-rectangle.h @@ -162,7 +162,8 @@ MTK_EXPORT gboolean mtk_rectangle_is_adjacent_to (const MtkRectangle *rect, const MtkRectangle *other); - +MTK_EXPORT +gboolean mtk_rectangle_is_empty (const MtkRectangle *rect); MTK_EXPORT void mtk_rectangle_transform (const MtkRectangle *rect, -- GitLab From a403ffc839fe8428e7f6a5bdf01bf8c595ea8614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 23 Apr 2025 14:24:35 +0200 Subject: [PATCH 15/38] window: Replace calc_placement field with place flag It's always toggled before and after a call to move_resize_internal(), so just pass it as a flag instead. Part-of: --- src/core/constraints.c | 2 +- src/core/window-private.h | 11 ++++++----- src/core/window.c | 19 +++---------------- src/wayland/meta-window-wayland.c | 19 +++++++++++-------- 4 files changed, 21 insertions(+), 30 deletions(-) diff --git a/src/core/constraints.c b/src/core/constraints.c index fa7d2e0d6b9..001c30920cc 100644 --- a/src/core/constraints.c +++ b/src/core/constraints.c @@ -549,7 +549,7 @@ place_window_if_needed (MetaWindow *window, */ did_placement = FALSE; if (!window->placed && - window->calc_placement && + place_flags & META_PLACE_FLAG_CALCULATE && !meta_window_config_is_any_maximized (window->config) && !window->minimized && !meta_window_is_fullscreen (window)) diff --git a/src/core/window-private.h b/src/core/window-private.h index c780c774a09..7d8b57d52ed 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -80,6 +80,7 @@ typedef enum _MetaPlaceFlag META_PLACE_FLAG_NONE = 0, META_PLACE_FLAG_FORCE_MOVE = 1 << 0, META_PLACE_FLAG_DENIED_FOCUS_AND_NOT_TRANSIENT = 1 << 1, + META_PLACE_FLAG_CALCULATE = 1 << 2, } MetaPlaceFlag; typedef enum @@ -524,11 +525,6 @@ struct _MetaWindow */ guint withdrawn : 1; - /* TRUE if constrain_position should calc placement. - * only relevant if !window->placed - */ - guint calc_placement : 1; - /* if TRUE, window is attached to its parent */ guint attached : 1; @@ -785,6 +781,11 @@ void meta_window_move_resize (MetaWindow *window, MetaMoveResizeFlags flags, MtkRectangle frame_rect); +void meta_window_move_resize_internal (MetaWindow *window, + MetaMoveResizeFlags flags, + MetaPlaceFlag place_flags, + MtkRectangle frame_rect); + void meta_window_grab_op_began (MetaWindow *window, MetaGrabOp op); void meta_window_grab_op_ended (MetaWindow *window, MetaGrabOp op); diff --git a/src/core/window.c b/src/core/window.c index 0e8da9deaba..6780126bbf9 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -177,11 +177,6 @@ static void set_hidden_suspended_state (MetaWindow *window); static void initable_iface_init (GInitableIface *initable_iface); -static void meta_window_move_resize_internal (MetaWindow *window, - MetaMoveResizeFlags flags, - MetaPlaceFlag place_flags, - MtkRectangle frame_rect); - typedef struct _MetaWindowPrivate { MetaQueueType queued_types; @@ -1139,7 +1134,6 @@ meta_window_constructed (GObject *object) window->initial_timestamp_set = FALSE; window->net_wm_user_time_set = FALSE; window->input = TRUE; - window->calc_placement = FALSE; window->unmaps_pending = 0; window->reparents_pending = 0; @@ -2185,12 +2179,6 @@ meta_window_force_placement (MetaWindow *window, * have been mapped/placed since we last did constrain_position */ - /* calc_placement is an efficiency hack to avoid - * multiple placement calculations before we finally - * show the window. - */ - window->calc_placement = TRUE; - flags = (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION | META_MOVE_RESIZE_CONSTRAIN); @@ -2199,9 +2187,8 @@ meta_window_force_placement (MetaWindow *window, meta_window_move_resize_internal (window, flags, - place_flags, + place_flags | META_PLACE_FLAG_CALCULATE, window->unconstrained_rect); - window->calc_placement = FALSE; /* don't ever do the initial position constraint thing again. * This is toggled here so that initially-iconified windows @@ -4055,7 +4042,7 @@ meta_window_update_monitor (MetaWindow *window, g_signal_emit (window, window_signals[HIGHEST_SCALE_MONITOR_CHANGED], 0); } -static void +void meta_window_move_resize_internal (MetaWindow *window, MetaMoveResizeFlags flags, MetaPlaceFlag place_flags, @@ -4102,7 +4089,7 @@ meta_window_move_resize_internal (MetaWindow *window, META_MOVE_RESIZE_RESIZE_ACTION | META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE)); - did_placement = !window->placed && window->calc_placement; + did_placement = !window->placed && (place_flags & META_PLACE_FLAG_CALCULATE); gravity = meta_window_get_gravity (window); diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c index 5f6b34bca0b..586ee79704c 100644 --- a/src/wayland/meta-window-wayland.c +++ b/src/wayland/meta-window-wayland.c @@ -1353,6 +1353,7 @@ meta_window_place_with_placement_rule (MetaWindow *window, MetaPlacementRule *placement_rule) { gboolean first_placement; + MetaPlaceFlag place_flags = META_PLACE_FLAG_NONE; first_placement = !window->placement.rule; @@ -1366,14 +1367,16 @@ meta_window_place_with_placement_rule (MetaWindow *window, window->unconstrained_rect.width = placement_rule->width; window->unconstrained_rect.height = placement_rule->height; - window->calc_placement = first_placement; - meta_window_move_resize (window, - (META_MOVE_RESIZE_MOVE_ACTION | - META_MOVE_RESIZE_RESIZE_ACTION | - META_MOVE_RESIZE_PLACEMENT_CHANGED | - META_MOVE_RESIZE_CONSTRAIN), - window->unconstrained_rect); - window->calc_placement = FALSE; + if (first_placement) + place_flags |= META_PLACE_FLAG_CALCULATE; + + meta_window_move_resize_internal (window, + (META_MOVE_RESIZE_MOVE_ACTION | + META_MOVE_RESIZE_RESIZE_ACTION | + META_MOVE_RESIZE_PLACEMENT_CHANGED | + META_MOVE_RESIZE_CONSTRAIN), + place_flags, + window->unconstrained_rect); } void -- GitLab From b879c391dd1b8b7c953f5eb6a8c07a8e16da4529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 23 Apr 2025 14:31:15 +0200 Subject: [PATCH 16/38] window/x11: Queue move-resize when finished reparenting This makes mapping a X11 window as maximized work more reliably. A test is added that checks that mapping a window maximized works for both Wayland and X11 clients. Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3385 Part-of: --- src/tests/meson.build | 1 + src/tests/stacking/map-maximized.metatest | 24 +++++++++++++++++++++++ src/x11/events.c | 6 +++++- 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/tests/stacking/map-maximized.metatest diff --git a/src/tests/meson.build b/src/tests/meson.build index 60d63eb8310..01b4b5c87a2 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -967,6 +967,7 @@ stacking_tests = [ 'strut-monitor-changes', 'window-placement', 'x11-move', + 'map-maximized', ] foreach stacking_test: stacking_tests diff --git a/src/tests/stacking/map-maximized.metatest b/src/tests/stacking/map-maximized.metatest new file mode 100644 index 00000000000..da28c7e802b --- /dev/null +++ b/src/tests/stacking/map-maximized.metatest @@ -0,0 +1,24 @@ +set_pref center-new-windows false +resize_monitor default 200 200 +new_client w wayland +new_client x x11 + +# Map a Wayland window maximized + +create w/1 csd +resize w/1 90 90 +maximize w/1 +show w/1 +wait_reconfigure w/1 +assert_size w/1 MONITOR_WIDTH MONITOR_HEIGHT +assert_position w/1 0 0 + +# Map a X11 window maximized + +create x/1 csd +resize x/1 90 90 +maximize x/1 +show x/1 +wait_reconfigure x/1 +assert_size x/1 MONITOR_WIDTH MONITOR_HEIGHT +assert_position x/1 0 0 diff --git a/src/x11/events.c b/src/x11/events.c index 4a9a43cbded..5b01ffadf92 100644 --- a/src/x11/events.c +++ b/src/x11/events.c @@ -1537,7 +1537,11 @@ handle_other_xevent (MetaX11Display *x11_display, case ReparentNotify: { if (window && window->reparents_pending > 0) - window->reparents_pending -= 1; + { + window->reparents_pending -= 1; + if (window->reparents_pending == 0) + meta_window_queue (window, META_QUEUE_MOVE_RESIZE); + } if (event->xreparent.event == x11_display->xroot) meta_stack_tracker_reparent_event (display->stack_tracker, &event->xreparent); -- GitLab From a85e4d3738b2d8639ed50bf26607a2880f6dc370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 23 Apr 2025 14:35:32 +0200 Subject: [PATCH 17/38] place: Place window according to new size Instead of using the current window size, apply placing algorithm using the to be constrained size. This allows placing a window with a new size before setting it as the current size. Currently this makes practically no difference, since we always place when showing a window, which we do when it already has a size, but it will matter in the future where we want to avoid placing when showing a window, in certain situations. Part-of: --- src/core/constraints.c | 1 + src/core/place.c | 48 ++++++++++++++++++++++++------------------ src/core/place.h | 2 ++ 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/core/constraints.c b/src/core/constraints.c index 001c30920cc..8aebc49ef9e 100644 --- a/src/core/constraints.c +++ b/src/core/constraints.c @@ -584,6 +584,7 @@ place_window_if_needed (MetaWindow *window, { meta_window_place (window, place_flags, orig_rect.x, orig_rect.y, + info->current.width, info->current.height, &placed_rect.x, &placed_rect.y); /* placing the window may have changed the monitor. Find the diff --git a/src/core/place.c b/src/core/place.c index aefa246c5e5..3b8488b1f79 100644 --- a/src/core/place.c +++ b/src/core/place.c @@ -167,6 +167,8 @@ static void find_next_cascade (MetaWindow *window, /* visible windows on relevant workspaces */ GList *windows, + int width, + int height, int *new_x, int *new_y, gboolean place_centered) @@ -204,15 +206,16 @@ find_next_cascade (MetaWindow *window, meta_window_get_work_area_for_logical_monitor (window, current, &work_area); meta_window_get_frame_rect (window, &frame_rect); - window_width = frame_rect.width; - window_height = frame_rect.height; + window_width = width; + window_height = height; sorted = g_list_copy (windows); if (place_centered) { WindowDistanceComparisonData window_distance_data = { .area = work_area, - .window = frame_rect, + .window = MTK_RECTANGLE_INIT (frame_rect.x, frame_rect.y, + width, height), .ltr = ltr, }; @@ -637,6 +640,8 @@ find_first_fit (MetaWindow *window, /* visible windows on relevant workspaces */ GList *windows, MetaLogicalMonitor *logical_monitor, + int width, + int height, int *new_x, int *new_y) { @@ -651,7 +656,7 @@ find_first_fit (MetaWindow *window, GList *below_sorted; GList *end_sorted; GList *tmp; - MtkRectangle rect; + MtkRectangle rect = MTK_RECTANGLE_INIT (0, 0, width, height); MtkRectangle work_area; gboolean ltr = clutter_get_text_direction () == CLUTTER_TEXT_DIRECTION_LTR; @@ -667,8 +672,6 @@ find_first_fit (MetaWindow *window, end_sorted = g_list_sort (end_sorted, topmost_cmp); end_sorted = g_list_sort (end_sorted, ltr ? leftmost_cmp : rightmost_cmp); - meta_window_get_frame_rect (window, &rect); - #ifdef WITH_VERBOSE_MODE { char monitor_location_string[RECT_LENGTH]; @@ -844,6 +847,8 @@ meta_window_place (MetaWindow *window, MetaPlaceFlag flags, int x, int y, + int new_width, + int new_height, int *new_x, int *new_y) { @@ -957,9 +962,8 @@ meta_window_place (MetaWindow *window, if (parent) { - MtkRectangle frame_rect, parent_frame_rect; + MtkRectangle parent_frame_rect; - meta_window_get_frame_rect (window, &frame_rect); meta_window_get_frame_rect (parent, &parent_frame_rect); y = parent_frame_rect.y; @@ -967,12 +971,12 @@ meta_window_place (MetaWindow *window, /* center of parent */ x = parent_frame_rect.x + parent_frame_rect.width / 2; /* center of child over center of parent */ - x -= frame_rect.width / 2; + x -= new_width / 2; /* "visually" center window over parent, leaving twice as * much space below as on top. */ - y += (parent_frame_rect.height - frame_rect.height) / 3; + y += (parent_frame_rect.height - new_height) / 3; meta_topic (META_DEBUG_PLACEMENT, "Centered window %s over transient parent", @@ -996,20 +1000,19 @@ meta_window_place (MetaWindow *window, { /* Center on current monitor */ MtkRectangle work_area; - MtkRectangle frame_rect; meta_window_get_work_area_for_logical_monitor (window, logical_monitor, &work_area); - meta_window_get_frame_rect (window, &frame_rect); - x = work_area.x + (work_area.width - frame_rect.width) / 2; - y = work_area.y + (work_area.height - frame_rect.height) / 2; + x = work_area.x + (work_area.width - new_width) / 2; + y = work_area.y + (work_area.height - new_height) / 2; meta_topic (META_DEBUG_PLACEMENT, "Centered window %s on monitor %d", window->desc, logical_monitor->number); - find_next_cascade (window, windows, &x, &y, place_centered); + find_next_cascade (window, windows, new_width, new_height, + &x, &y, place_centered); } else { @@ -1018,9 +1021,13 @@ meta_window_place (MetaWindow *window, y = logical_monitor->rect.y; /* No good fit? Fall back to cascading... */ - if (!find_first_fit (window, windows, logical_monitor, + if (!find_first_fit (window, windows, + logical_monitor, new_width, new_height, &x, &y)) - find_next_cascade (window, windows, &x, &y, place_centered); + { + find_next_cascade (window, windows, new_width, new_height, + &x, &y, place_centered); + } } /* Maximize windows if they are too big for their work area (bit of @@ -1031,17 +1038,15 @@ meta_window_place (MetaWindow *window, !meta_window_is_fullscreen (window)) { MtkRectangle workarea; - MtkRectangle frame_rect; meta_window_get_work_area_for_logical_monitor (window, logical_monitor, &workarea); - meta_window_get_frame_rect (window, &frame_rect); /* If the window is bigger than the screen, then automaximize. Do NOT * auto-maximize the directions independently. See #419810. */ - if (frame_rect.width >= workarea.width && frame_rect.height >= workarea.height) + if (new_width >= workarea.width && new_height >= workarea.height) { window->maximize_horizontally_after_placement = TRUE; window->maximize_vertically_after_placement = TRUE; @@ -1077,7 +1082,8 @@ meta_window_place (MetaWindow *window, y = logical_monitor->rect.y; found_fit = find_first_fit (window, focus_window_list, - logical_monitor, &x, &y); + logical_monitor, new_width, new_height, + &x, &y); g_list_free (focus_window_list); } diff --git a/src/core/place.h b/src/core/place.h index d29e711057a..b51b2528b98 100644 --- a/src/core/place.h +++ b/src/core/place.h @@ -32,5 +32,7 @@ void meta_window_place (MetaWindow *window, MetaPlaceFlag place_flags, int x, int y, + int new_width, + int new_height, int *new_x, int *new_y); -- GitLab From 738180e60e394dbcf3f3256a029e840a082aced3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 23 Apr 2025 21:04:32 +0200 Subject: [PATCH 18/38] window-config: Add helper to check if config is floating This currently means not fullscreen, and not partially/fully maximized. Part-of: --- src/core/meta-window-config-private.h | 2 ++ src/core/meta-window-config.c | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/core/meta-window-config-private.h b/src/core/meta-window-config-private.h index 93cf0c73aad..d950da5cd40 100644 --- a/src/core/meta-window-config-private.h +++ b/src/core/meta-window-config-private.h @@ -62,3 +62,5 @@ void meta_window_config_set_tile_hfraction (MetaWindowConfig *config, void meta_window_config_set_tile_match (MetaWindowConfig *config, MetaWindow *tile_match); + +gboolean meta_window_config_is_floating (MetaWindowConfig *config); diff --git a/src/core/meta-window-config.c b/src/core/meta-window-config.c index 7948c6c2c64..6d5fb4c2d90 100644 --- a/src/core/meta-window-config.c +++ b/src/core/meta-window-config.c @@ -295,6 +295,13 @@ meta_window_config_set_tile_match (MetaWindowConfig *config, config->tile_match = tile_match; } +gboolean +meta_window_config_is_floating (MetaWindowConfig *config) +{ + return (!config->is_fullscreen && + !meta_window_config_is_any_maximized (config)); +} + MetaWindowConfig * meta_window_config_new (void) { -- GitLab From 9be3f5f06d1ae236074268a412925a8c61fdedca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 23 Apr 2025 21:19:43 +0200 Subject: [PATCH 19/38] wayland/window-configuration: Track whether floating We could also in theory track being maximized/tiling/fullscreen, but currently only "is floating" will be needed, so lets not fully mirror all the non-floating configurations here yet. Part-of: --- src/wayland/meta-wayland-window-configuration.c | 5 +++++ src/wayland/meta-wayland-window-configuration.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/wayland/meta-wayland-window-configuration.c b/src/wayland/meta-wayland-window-configuration.c index 70340e63951..b8cae437a01 100644 --- a/src/wayland/meta-wayland-window-configuration.c +++ b/src/wayland/meta-wayland-window-configuration.c @@ -52,6 +52,7 @@ meta_wayland_window_configuration_new (MetaWindow *window, .flags = flags, .is_fullscreen = meta_window_is_fullscreen (window), + .is_floating = meta_window_config_is_floating (window->config), .is_suspended = meta_window_is_suspended (window), }; @@ -189,6 +190,10 @@ meta_wayland_window_configuration_apply_window_config (MetaWindow meta_window_config_set_is_fullscreen (window->config, is_fullscreen); configuration->is_fullscreen = is_fullscreen; + configuration->is_floating = + (!meta_window_config_get_is_fullscreen (window_config) && + !meta_window_config_is_any_maximized (window_config)); + if (prev_x != configuration->x || prev_y != configuration->y) { configuration->has_position = TRUE; diff --git a/src/wayland/meta-wayland-window-configuration.h b/src/wayland/meta-wayland-window-configuration.h index e969662df33..78b8c2bb293 100644 --- a/src/wayland/meta-wayland-window-configuration.h +++ b/src/wayland/meta-wayland-window-configuration.h @@ -52,6 +52,7 @@ struct _MetaWaylandWindowConfiguration int bounds_height; gboolean is_fullscreen; + gboolean is_floating; gboolean is_suspended; }; -- GitLab From 3be73569fe757222b218d07361996828402e5657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 28 Apr 2025 17:25:30 +0200 Subject: [PATCH 20/38] window/x11: Only use rect for construct-set monitor for USPosition The monitor used for placing and constraining should be up to window management policy - unless the geometry was set with USPosition, which means it'd was explicitly set with e.g. `-geometry WIDTHxHEIGHT+X+Y`. Part-of: --- src/core/window.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/window.c b/src/core/window.c index 6780126bbf9..a612296ae7f 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -1185,7 +1185,8 @@ meta_window_constructed (GObject *object) window->compositor_private = NULL; - if (frame_rect.width > 0 && frame_rect.height > 0) + if (frame_rect.width > 0 && frame_rect.height > 0 && + (window->size_hints.flags & META_SIZE_HINTS_USER_POSITION)) { window->monitor = meta_window_find_monitor_from_frame_rect (window); window->highest_scale_monitor = -- GitLab From f2db50de7795252a4b2ac46407dae231945978fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 25 Apr 2025 23:24:28 +0200 Subject: [PATCH 21/38] window: Remove enum alignment from move resize result Will make it easier to add more enum values without having to realign. Part-of: --- src/core/window-private.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/window-private.h b/src/core/window-private.h index 7d8b57d52ed..4e614244752 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -85,9 +85,9 @@ typedef enum _MetaPlaceFlag typedef enum { - META_MOVE_RESIZE_RESULT_MOVED = 1 << 0, - META_MOVE_RESIZE_RESULT_RESIZED = 1 << 1, - META_MOVE_RESIZE_RESULT_STATE_CHANGED = 1 << 3, + META_MOVE_RESIZE_RESULT_MOVED = 1 << 0, + META_MOVE_RESIZE_RESULT_RESIZED = 1 << 1, + META_MOVE_RESIZE_RESULT_STATE_CHANGED = 1 << 3, } MetaMoveResizeResultFlags; typedef enum -- GitLab From 08ffdd5b365e51bb3bf9c83efe073bb764056794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 28 Apr 2025 14:01:42 +0200 Subject: [PATCH 22/38] window: Move Wayland specific detail to Wayland subclass With Wayland, we only want to updated the "unconstrained" rect in certain situations. Lets keep details about when in the Wayland subclass. Part-of: --- src/core/window-private.h | 1 + src/core/window.c | 9 ++------- src/wayland/meta-window-wayland.c | 4 ++++ src/x11/window-x11.c | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/core/window-private.h b/src/core/window-private.h index 4e614244752..514753f1c2d 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -88,6 +88,7 @@ typedef enum META_MOVE_RESIZE_RESULT_MOVED = 1 << 0, META_MOVE_RESIZE_RESULT_RESIZED = 1 << 1, META_MOVE_RESIZE_RESULT_STATE_CHANGED = 1 << 3, + META_MOVE_RESIZE_RESULT_UPDATE_UNCONSTRAINED = 1 << 4, } MetaMoveResizeResultFlags; typedef enum diff --git a/src/core/window.c b/src/core/window.c index a612296ae7f..5e05f04ada0 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -4190,13 +4190,8 @@ meta_window_move_resize_internal (MetaWindow *window, g_signal_emit (window, window_signals[SIZE_CHANGED], 0); } - /* Only update the stored size when requested but not when a - * (potentially outdated) request completes */ - if (!(flags & META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE) || - flags & META_MOVE_RESIZE_WAYLAND_CLIENT_RESIZE) - { - window->unconstrained_rect = unconstrained_rect; - } + if (result & META_MOVE_RESIZE_RESULT_UPDATE_UNCONSTRAINED) + window->unconstrained_rect = unconstrained_rect; if ((moved_or_resized || did_placement || diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c index 586ee79704c..fe5d276c3a7 100644 --- a/src/wayland/meta-window-wayland.c +++ b/src/wayland/meta-window-wayland.c @@ -482,6 +482,10 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, if (can_move_now && flags & META_MOVE_RESIZE_WAYLAND_STATE_CHANGED) *result |= META_MOVE_RESIZE_RESULT_STATE_CHANGED; + + if (flags & META_MOVE_RESIZE_WAYLAND_CLIENT_RESIZE || + !(flags & META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE)) + *result |= META_MOVE_RESIZE_RESULT_UPDATE_UNCONSTRAINED; } static void diff --git a/src/x11/window-x11.c b/src/x11/window-x11.c index fc0f88a9f39..3297e3b8f58 100644 --- a/src/x11/window-x11.c +++ b/src/x11/window-x11.c @@ -1632,6 +1632,7 @@ meta_window_x11_move_resize_internal (MetaWindow *window, *result |= META_MOVE_RESIZE_RESULT_RESIZED; if (flags & META_MOVE_RESIZE_STATE_CHANGED) *result |= META_MOVE_RESIZE_RESULT_STATE_CHANGED; + *result |= META_MOVE_RESIZE_RESULT_UPDATE_UNCONSTRAINED; update_gtk_edge_constraints (window); } -- GitLab From f5601ce903eb81e73fe42042d90ae48a1ecb7e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 28 Apr 2025 14:06:18 +0200 Subject: [PATCH 23/38] window: Update comment to clarify what "placed" means Part-of: --- src/core/window-private.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/window-private.h b/src/core/window-private.h index 514753f1c2d..67d3755d87b 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -509,7 +509,8 @@ struct _MetaWindow /* TRUE if window appears focused at the moment */ guint appears_focused : 1; - /* Have we placed this window? */ + /* Have we placed this window according to the floating window placement + * algorithm? */ guint placed : 1; /* Has this window not ever been shown yet? */ -- GitLab From e2bb1b8012f8339d6423fbe014cafd621490842d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 28 Apr 2025 14:44:59 +0200 Subject: [PATCH 24/38] window: Keep track of when the unconstrained rect is valid It's initially invalid, and then made valid when it's explicitly set by something. This will allow avoiding its usage when it doesn't contain anything reliable. Part-of: --- src/core/constraints.c | 1 + src/core/window-private.h | 3 +++ src/core/window.c | 6 +++++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/core/constraints.c b/src/core/constraints.c index 8aebc49ef9e..b2572d3b525 100644 --- a/src/core/constraints.c +++ b/src/core/constraints.c @@ -637,6 +637,7 @@ place_window_if_needed (MetaWindow *window, * uses the placed coordinates (bug #556696). */ window->unconstrained_rect = info->current; + window->unconstrained_rect_valid = TRUE; meta_window_maximize_internal (window, (window->maximize_horizontally_after_placement ? diff --git a/src/core/window-private.h b/src/core/window-private.h index 67d3755d87b..98d7772c5d6 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -513,6 +513,9 @@ struct _MetaWindow * algorithm? */ guint placed : 1; + /* Have this window been positioned? */ + uint unconstrained_rect_valid : 1; + /* Has this window not ever been shown yet? */ guint showing_for_first_time : 1; diff --git a/src/core/window.c b/src/core/window.c index 5e05f04ada0..9e51834afff 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -4191,7 +4191,10 @@ meta_window_move_resize_internal (MetaWindow *window, } if (result & META_MOVE_RESIZE_RESULT_UPDATE_UNCONSTRAINED) - window->unconstrained_rect = unconstrained_rect; + { + window->unconstrained_rect = unconstrained_rect; + window->unconstrained_rect_valid = TRUE; + } if ((moved_or_resized || did_placement || @@ -4338,6 +4341,7 @@ meta_window_move_between_rects (MetaWindow *window, window->unconstrained_rect.x = new_x; window->unconstrained_rect.y = new_y; + window->unconstrained_rect_valid = TRUE; meta_window_move_resize (window, (move_resize_flags | -- GitLab From 2f0895df090479894d2a29bbbe20d2a58e882021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 28 Apr 2025 17:23:47 +0200 Subject: [PATCH 25/38] window: Add move/resize flag for invalid target rects Sometimes the passed rectangle is not valid, e.g. an nonsensical / non-placed unconstrained rectangle. Pass this information down the line to meta_window_move_resize_internal() so that using the target rectangle can be limited. Part-of: --- src/core/window-private.h | 1 + src/core/window.c | 49 ++++++++++++++++++++----------- src/wayland/meta-window-wayland.c | 14 ++++++++- src/x11/window-x11.c | 7 ++++- 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/core/window-private.h b/src/core/window-private.h index 98d7772c5d6..a116fbe8352 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -73,6 +73,7 @@ typedef enum META_MOVE_RESIZE_PLACEMENT_CHANGED = 1 << 11, META_MOVE_RESIZE_WAYLAND_CLIENT_RESIZE = 1 << 12, META_MOVE_RESIZE_CONSTRAIN = 1 << 13, + META_MOVE_RESIZE_RECT_INVALID = 1 << 14, } MetaMoveResizeFlags; typedef enum _MetaPlaceFlag diff --git a/src/core/window.c b/src/core/window.c index 9e51834afff..be1e9f82ad2 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -2869,6 +2869,8 @@ meta_window_maximize (MetaWindow *window, if ((maximize_horizontally && !was_maximized_horizontally) || (maximize_vertically && !was_maximized_vertically)) { + MetaMoveResizeFlags flags; + /* if the window hasn't been placed yet, we'll maximize it then */ if (!window->placed) @@ -2905,12 +2907,14 @@ meta_window_maximize (MetaWindow *window, META_SIZE_CHANGE_MAXIMIZE, &old_frame_rect, &old_buffer_rect); - meta_window_move_resize (window, - (META_MOVE_RESIZE_MOVE_ACTION | - META_MOVE_RESIZE_RESIZE_ACTION | - META_MOVE_RESIZE_STATE_CHANGED | - META_MOVE_RESIZE_CONSTRAIN), - window->unconstrained_rect); + flags = (META_MOVE_RESIZE_MOVE_ACTION | + META_MOVE_RESIZE_RESIZE_ACTION | + META_MOVE_RESIZE_STATE_CHANGED | + META_MOVE_RESIZE_CONSTRAIN); + if (!window->unconstrained_rect_valid) + flags |= META_MOVE_RESIZE_RECT_INVALID; + + meta_window_move_resize (window, flags, window->unconstrained_rect); } } @@ -3559,6 +3563,7 @@ meta_window_make_fullscreen (MetaWindow *window) if (!meta_window_is_fullscreen (window)) { MtkRectangle old_frame_rect, old_buffer_rect; + MetaMoveResizeFlags flags; meta_window_get_frame_rect (window, &old_frame_rect); meta_window_get_buffer_rect (window, &old_buffer_rect); @@ -3568,12 +3573,14 @@ meta_window_make_fullscreen (MetaWindow *window) &old_frame_rect, &old_buffer_rect); meta_window_make_fullscreen_internal (window); - meta_window_move_resize (window, - (META_MOVE_RESIZE_MOVE_ACTION | - META_MOVE_RESIZE_RESIZE_ACTION | - META_MOVE_RESIZE_STATE_CHANGED | - META_MOVE_RESIZE_CONSTRAIN), - window->unconstrained_rect); + + flags = (META_MOVE_RESIZE_MOVE_ACTION | + META_MOVE_RESIZE_RESIZE_ACTION | + META_MOVE_RESIZE_STATE_CHANGED | + META_MOVE_RESIZE_CONSTRAIN); + if (!window->unconstrained_rect_valid) + flags |= META_MOVE_RESIZE_RECT_INVALID; + meta_window_move_resize (window, flags, window->unconstrained_rect); } } @@ -4512,11 +4519,19 @@ meta_window_resize_frame (MetaWindow *window, void meta_window_idle_move_resize (MetaWindow *window) { - meta_window_move_resize_frame (window, FALSE, - window->unconstrained_rect.x, - window->unconstrained_rect.y, - window->unconstrained_rect.width, - window->unconstrained_rect.height); + MetaMoveResizeFlags flags; + + flags = (META_MOVE_RESIZE_MOVE_ACTION | + META_MOVE_RESIZE_RESIZE_ACTION | + META_MOVE_RESIZE_CONSTRAIN); + if (!window->unconstrained_rect_valid) + flags |= META_MOVE_RESIZE_RECT_INVALID; + meta_window_move_resize (window, + META_MOVE_RESIZE_MOVE_ACTION | + META_MOVE_RESIZE_RESIZE_ACTION | + META_MOVE_RESIZE_CONSTRAIN | + META_MOVE_RESIZE_RECT_INVALID, + window->unconstrained_rect); } gboolean diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c index fe5d276c3a7..84d653b0e16 100644 --- a/src/wayland/meta-window-wayland.c +++ b/src/wayland/meta-window-wayland.c @@ -1210,6 +1210,7 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, MetaWaylandSurfaceState *pending) { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); + gboolean has_position = FALSE; MetaDisplay *display = window->display; MetaWaylandSurface *surface = wl_window->surface; int dx, dy; @@ -1297,9 +1298,17 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, if (acked_configuration->is_fullscreen) flags |= META_MOVE_RESIZE_CONSTRAIN; if (acked_configuration->has_position) - calculate_position (acked_configuration, &new_geom, &rect); + { + has_position = TRUE; + calculate_position (acked_configuration, &new_geom, &rect); + } } } + else + { + if (window->placed) + has_position = TRUE; + } } else { @@ -1307,6 +1316,9 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, calculate_position (acked_configuration, &new_geom, &rect); } + if (!has_position) + flags |= META_MOVE_RESIZE_RECT_INVALID; + toplevel_drag = get_toplevel_drag (window); if (toplevel_drag && !is_window_being_resized && !window->mapped && rect.width > 0 && rect.height > 0) diff --git a/src/x11/window-x11.c b/src/x11/window-x11.c index 3297e3b8f58..2f04533d317 100644 --- a/src/x11/window-x11.c +++ b/src/x11/window-x11.c @@ -688,6 +688,7 @@ meta_window_x11_initialize_state (MetaWindow *window) MtkRectangle rect; MetaMoveResizeFlags flags; MetaGravity gravity = window->size_hints.win_gravity; + MetaPlaceFlag place_flags = META_PLACE_FLAG_NONE; rect.x = window->size_hints.x; rect.y = window->size_hints.y; @@ -699,9 +700,13 @@ meta_window_x11_initialize_state (MetaWindow *window) META_MOVE_RESIZE_RESIZE_ACTION | META_MOVE_RESIZE_CONSTRAIN); + if (!(window->size_hints.flags & META_SIZE_HINTS_USER_POSITION)) + flags |= META_MOVE_RESIZE_RECT_INVALID; + adjust_for_gravity (window, TRUE, gravity, &rect); meta_window_client_rect_to_frame_rect (window, &rect, &rect); - meta_window_move_resize (window, flags, rect); + + meta_window_move_resize_internal (window, flags, place_flags, rect); } meta_window_x11_update_shape_region (window); -- GitLab From 9f1d8418d0ea30905a3e5fb5aa643430f6ab11e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 28 Apr 2025 17:05:56 +0200 Subject: [PATCH 26/38] window: Don't update saved rect when unmaximizing We'd set up a "desired rect" that in fact is a pointer to the saved rect, and manipulate it according to some heuristics about auto-maximizing. Let's changed that to not updating the saved rect, leaving it intact for future use. The automaximize heuristics is still in place, but should perhaps be only enabled with the automaximize preference turned on. Part-of: --- src/core/window.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/core/window.c b/src/core/window.c index be1e9f82ad2..c4282393297 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -3377,7 +3377,7 @@ meta_window_unmaximize (MetaWindow *window, if ((unmaximize_horizontally && was_maximized_horizontally) || (unmaximize_vertically && was_maximized_vertically)) { - MtkRectangle *desired_rect; + MtkRectangle desired_rect; MtkRectangle target_rect; MtkRectangle work_area; MtkRectangle old_frame_rect, old_buffer_rect; @@ -3411,7 +3411,7 @@ meta_window_unmaximize (MetaWindow *window, */ meta_window_frame_size_changed (window); - desired_rect = &window->saved_rect; + desired_rect = window->saved_rect; /* Unmaximize to the saved_rect position in the direction(s) * being unmaximized. @@ -3423,44 +3423,44 @@ meta_window_unmaximize (MetaWindow *window, * the work area as upper limit while maintaining the aspect ratio. */ if (unmaximize_horizontally && unmaximize_vertically && - desired_rect->width * desired_rect->height > + desired_rect.width * desired_rect.height > work_area.width * work_area.height * MAX_UNMAXIMIZED_WINDOW_AREA) { - if (desired_rect->width > desired_rect->height) + if (desired_rect.width > desired_rect.height) { float aspect; - aspect = (float) desired_rect->height / (float) desired_rect->width; - desired_rect->width = + aspect = (float) desired_rect.height / (float) desired_rect.width; + desired_rect.width = (int) MAX (work_area.width * sqrt (MAX_UNMAXIMIZED_WINDOW_AREA), window->size_hints.min_width); - desired_rect->height = - (int) MAX (desired_rect->width * aspect, + desired_rect.height = + (int) MAX (desired_rect.width * aspect, window->size_hints.min_height); } else { float aspect; - aspect = (float) desired_rect->width / (float) desired_rect->height; - desired_rect->height = + aspect = (float) desired_rect.width / (float) desired_rect.height; + desired_rect.height = (int) MAX (work_area.height * sqrt (MAX_UNMAXIMIZED_WINDOW_AREA), window->size_hints.min_height); - desired_rect->width = - (int) MAX (desired_rect->height * aspect, + desired_rect.width = + (int) MAX (desired_rect.height * aspect, window->size_hints.min_width); } } if (unmaximize_horizontally) { - target_rect.x = desired_rect->x; - target_rect.width = desired_rect->width; + target_rect.x = desired_rect.x; + target_rect.width = desired_rect.width; } if (unmaximize_vertically) { - target_rect.y = desired_rect->y; - target_rect.height = desired_rect->height; + target_rect.y = desired_rect.y; + target_rect.height = desired_rect.height; } /* Window's size hints may have changed while maximized, making -- GitLab From e773cf72ddafebfb6b6341ec0985fa81dced2d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 23 Apr 2025 21:16:11 +0200 Subject: [PATCH 27/38] tests/test-runner: Add 'resize_ignore_titlebar' This is a work around for gtk treating gtk_window_resize() differently when fullscreen and maximized, before being mapped. To achieve the same size when going floating, the titlebar need to either be taken into account, or ignored, depending on the next step. This command should be removed once test clients can more easily be fixed. Part-of: --- src/tests/test-client.c | 14 +++++++++++--- src/tests/test-runner.c | 3 ++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/tests/test-client.c b/src/tests/test-client.c index 05bece80082..d5b1dbc4679 100644 --- a/src/tests/test-client.c +++ b/src/tests/test-client.c @@ -593,11 +593,14 @@ process_line (const char *line) gtk_window_present (GTK_WINDOW (window)); } - else if (strcmp (argv[0], "resize") == 0) + else if (strcmp (argv[0], "resize") == 0 || + strcmp (argv[0], "resize_ignore_titlebar") == 0) { + int titlebar_height; + if (argc != 4) { - g_print ("usage: resize \n"); + g_print ("usage: %s \n", argv[0]); goto out; } @@ -607,7 +610,12 @@ process_line (const char *line) int width = atoi (argv[2]); int height = atoi (argv[3]); - int titlebar_height = calculate_titlebar_height (GTK_WINDOW (window)); + + if (strcmp (argv[0], "resize_ignore_titlebar") == 0) + titlebar_height = 0; + else + titlebar_height = calculate_titlebar_height (GTK_WINDOW (window)); + gtk_window_resize (GTK_WINDOW (window), width, height - titlebar_height); diff --git a/src/tests/test-runner.c b/src/tests/test-runner.c index 6ed4aa56393..10a32c1b7e1 100644 --- a/src/tests/test-runner.c +++ b/src/tests/test-runner.c @@ -1110,7 +1110,8 @@ test_case_do (TestCase *test, meta_wait_for_window_shown (window); } - else if (strcmp (argv[0], "resize") == 0) + else if (strcmp (argv[0], "resize") == 0 || + strcmp (argv[0], "resize_ignore_titlebar") == 0) { if (argc != 4) BAD_COMMAND("usage: %s / width height", argv[0]); -- GitLab From 2682da09f1140df3f20e4ac24bf8ade15ba7e245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 23 Apr 2025 14:41:05 +0200 Subject: [PATCH 28/38] window: Wait with placing a non-floating window A maximized or fullscreen window doesn't have a known floating size, so placing it at this point, or saving a restored rect, isn't possible without making up our own floating size. To not have to make up our own size, we must postpone placing a window that was mapped non-floating until it's floating. In the Wayland case, that means waiting until the clients responds to a floating configure event with a committed floating window state. The test cases testing mapping as maximized is extended to test this case as well. Another equivalent test case for fullscreen cases is added. This also means some changes to how the monitor used for constraining or placing is determined. For placing, the main monitor of the window is used, unless the window is currently showing for the first time. This is important due to the non-atomic nature of X11 window management, where a window is configured step by step. When constraining we always use the main monitor of the window if the passed rectangle is not valid (e.g. an uninitialized unconstrained window layout rectangle), and the window determined by the target window layout rectangle if it is valid. The saved rectangles are no longer saved on construct, so to maintain some X11 specific behavior, the unmaximize and unfullscreen functions was adapted to handle the case where we'd try to unmaximize/unfullscreen to something where the client had already passed an unconstrained rectangle. Part-of: --- src/core/constraints.c | 19 +++- src/core/place.c | 46 ++++---- src/core/window.c | 108 +++++++++++-------- src/tests/meson.build | 1 + src/tests/stacking/map-fullscreen.metatest | 52 +++++++++ src/tests/stacking/map-maximized.metatest | 38 ++++++- src/wayland/meta-wayland-xdg-session-state.c | 2 +- src/wayland/meta-wayland-xdg-shell.c | 1 - src/wayland/meta-window-wayland.c | 25 ++++- src/x11/window-props.c | 10 +- 10 files changed, 218 insertions(+), 84 deletions(-) create mode 100644 src/tests/stacking/map-fullscreen.metatest diff --git a/src/core/constraints.c b/src/core/constraints.c index b2572d3b525..efa8c4cafe7 100644 --- a/src/core/constraints.c +++ b/src/core/constraints.c @@ -363,7 +363,7 @@ setup_constraint_info (MetaBackend *backend, { MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); - MetaLogicalMonitor *logical_monitor; + MetaLogicalMonitor *logical_monitor = NULL; MetaWorkspace *cur_workspace; MetaPlacementRule *placement_rule; @@ -446,9 +446,20 @@ setup_constraint_info (MetaBackend *backend, } else { - logical_monitor = - meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager, - &info->current); + if (!(flags & META_MOVE_RESIZE_RECT_INVALID)) + { + meta_topic (META_DEBUG_GEOMETRY, + "Constraining using monitor from new rectangle"); + logical_monitor = + meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager, + &info->current); + } + + if (!logical_monitor) + { + meta_topic (META_DEBUG_GEOMETRY, "Constraining using window monitor"); + logical_monitor = window->monitor; + } } if (!logical_monitor) diff --git a/src/core/place.c b/src/core/place.c index 3b8488b1f79..2718c5f525f 100644 --- a/src/core/place.c +++ b/src/core/place.c @@ -164,18 +164,16 @@ northeast_cmp (gconstpointer a, } static void -find_next_cascade (MetaWindow *window, +find_next_cascade (MetaWindow *window, + MtkRectangle work_area, /* visible windows on relevant workspaces */ - GList *windows, - int width, - int height, - int *new_x, - int *new_y, - gboolean place_centered) + GList *windows, + int width, + int height, + int *new_x, + int *new_y, + gboolean place_centered) { - MetaDisplay *display = meta_window_get_display (window); - MetaContext *context = meta_display_get_context (display); - MetaBackend *backend = meta_context_get_backend (context); GList *tmp; GList *sorted; int adjusted_center_x, adjusted_center_y; @@ -184,8 +182,6 @@ find_next_cascade (MetaWindow *window, MtkRectangle frame_rect; int window_width, window_height; int cascade_stage; - MtkRectangle work_area; - MetaLogicalMonitor *current; gboolean ltr = clutter_get_text_direction () == CLUTTER_TEXT_DIRECTION_LTR; /* This is a "fuzzy" cascade algorithm. @@ -202,9 +198,6 @@ find_next_cascade (MetaWindow *window, * of NW corner of window frame. */ - current = meta_backend_get_current_logical_monitor (backend); - meta_window_get_work_area_for_logical_monitor (window, current, &work_area); - meta_window_get_frame_rect (window, &frame_rect); window_width = width; window_height = height; @@ -858,6 +851,7 @@ meta_window_place (MetaWindow *window, g_autoptr (GList) windows = NULL; MetaLogicalMonitor *logical_monitor = NULL; gboolean place_centered = FALSE; + MtkRectangle work_area; meta_topic (META_DEBUG_PLACEMENT, "Placing window %s", window->desc); @@ -993,25 +987,27 @@ meta_window_place (MetaWindow *window, */ windows = find_windows_relevant_for_placement (window); - logical_monitor = meta_backend_get_current_logical_monitor (backend); + + if (!window->showing_for_first_time) + logical_monitor = meta_window_get_main_logical_monitor (window); + else + logical_monitor = meta_backend_get_current_logical_monitor (backend); + + meta_window_get_work_area_for_logical_monitor (window, + logical_monitor, + &work_area); + place_centered = window_place_centered (window); if (place_centered) { - /* Center on current monitor */ - MtkRectangle work_area; - - meta_window_get_work_area_for_logical_monitor (window, - logical_monitor, - &work_area); - x = work_area.x + (work_area.width - new_width) / 2; y = work_area.y + (work_area.height - new_height) / 2; meta_topic (META_DEBUG_PLACEMENT, "Centered window %s on monitor %d", window->desc, logical_monitor->number); - find_next_cascade (window, windows, new_width, new_height, + find_next_cascade (window, work_area, windows, new_width, new_height, &x, &y, place_centered); } else @@ -1025,7 +1021,7 @@ meta_window_place (MetaWindow *window, logical_monitor, new_width, new_height, &x, &y)) { - find_next_cascade (window, windows, new_width, new_height, + find_next_cascade (window, work_area, windows, new_width, new_height, &x, &y, place_centered); } } diff --git a/src/core/window.c b/src/core/window.c index c4282393297..225cb515ebe 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -1098,10 +1098,7 @@ meta_window_constructed (GObject *object) /* initialize the remaining size_hints as if size_hints.flags were zero */ meta_window_set_normal_hints (window, NULL); - /* And this is our unmaximized size */ frame_rect = meta_window_config_get_rect (window->config); - window->saved_rect = frame_rect; - window->saved_rect_fullscreen = frame_rect; window->unconstrained_rect = frame_rect; window->title = NULL; @@ -2389,7 +2386,8 @@ meta_window_show (MetaWindow *window) place_flags |= META_PLACE_FLAG_DENIED_FOCUS_AND_NOT_TRANSIENT; } - if (!window->placed) + if (!window->placed && + meta_window_config_is_floating (window->config)) { if (window->monitor && meta_prefs_get_auto_maximize () && @@ -2871,19 +2869,6 @@ meta_window_maximize (MetaWindow *window, { MetaMoveResizeFlags flags; - /* if the window hasn't been placed yet, we'll maximize it then - */ - if (!window->placed) - { - window->maximize_horizontally_after_placement = - window->maximize_horizontally_after_placement || - maximize_horizontally; - window->maximize_vertically_after_placement = - window->maximize_vertically_after_placement || - maximize_vertically; - return; - } - if (meta_window_config_get_tile_mode (window->config) != META_TILE_NONE) { saved_rect = &window->saved_rect; @@ -3378,10 +3363,16 @@ meta_window_unmaximize (MetaWindow *window, (unmaximize_vertically && was_maximized_vertically)) { MtkRectangle desired_rect; + gboolean has_desired_rect = FALSE; MtkRectangle target_rect; MtkRectangle work_area; MtkRectangle old_frame_rect, old_buffer_rect; gboolean has_target_size; + MetaPlaceFlag place_flags = META_PLACE_FLAG_NONE; + MetaMoveResizeFlags flags = (META_MOVE_RESIZE_MOVE_ACTION | + META_MOVE_RESIZE_RESIZE_ACTION | + META_MOVE_RESIZE_STATE_CHANGED | + META_MOVE_RESIZE_UNMAXIMIZE); meta_window_get_work_area_current_monitor (window, &work_area); meta_window_get_frame_rect (window, &old_frame_rect); @@ -3411,18 +3402,34 @@ meta_window_unmaximize (MetaWindow *window, */ meta_window_frame_size_changed (window); - desired_rect = window->saved_rect; + if (!window->placed && + !mtk_rectangle_is_empty (&window->unconstrained_rect)) + { + place_flags |= META_PLACE_FLAG_CALCULATE; + flags |= META_MOVE_RESIZE_CONSTRAIN; - /* Unmaximize to the saved_rect position in the direction(s) - * being unmaximized. - */ - target_rect = old_frame_rect; + if (!window->unconstrained_rect_valid) + flags |= META_MOVE_RESIZE_RECT_INVALID; + + target_rect = window->unconstrained_rect; + } + else + { + desired_rect = window->saved_rect; + has_desired_rect = TRUE; + + /* Unmaximize to the saved_rect position in the direction(s) + * being unmaximized. + */ + target_rect = old_frame_rect; + } /* Avoid unmaximizing to "almost maximized" size when the previous size * is greater then 80% of the work area use MAX_UNMAXIMIZED_WINDOW_AREA of * the work area as upper limit while maintaining the aspect ratio. */ if (unmaximize_horizontally && unmaximize_vertically && + has_desired_rect && desired_rect.width * desired_rect.height > work_area.width * work_area.height * MAX_UNMAXIMIZED_WINDOW_AREA) { @@ -3452,15 +3459,18 @@ meta_window_unmaximize (MetaWindow *window, } } - if (unmaximize_horizontally) + if (has_desired_rect) { - target_rect.x = desired_rect.x; - target_rect.width = desired_rect.width; - } - if (unmaximize_vertically) - { - target_rect.y = desired_rect.y; - target_rect.height = desired_rect.height; + if (unmaximize_horizontally) + { + target_rect.x = desired_rect.x; + target_rect.width = desired_rect.width; + } + if (unmaximize_vertically) + { + target_rect.y = desired_rect.y; + target_rect.height = desired_rect.height; + } } /* Window's size hints may have changed while maximized, making @@ -3475,12 +3485,8 @@ meta_window_unmaximize (MetaWindow *window, META_SIZE_CHANGE_UNMAXIMIZE, &old_frame_rect, &old_buffer_rect); - meta_window_move_resize (window, - (META_MOVE_RESIZE_MOVE_ACTION | - META_MOVE_RESIZE_RESIZE_ACTION | - META_MOVE_RESIZE_STATE_CHANGED | - META_MOVE_RESIZE_UNMAXIMIZE), - target_rect); + meta_window_move_resize_internal (window, flags, place_flags, + target_rect); meta_window_recalc_features (window); set_net_wm_state (window); @@ -3594,12 +3600,32 @@ meta_window_unmake_fullscreen (MetaWindow *window) { MtkRectangle old_frame_rect, old_buffer_rect, target_rect; gboolean has_target_size; + MetaPlaceFlag place_flags = META_PLACE_FLAG_NONE; + MetaMoveResizeFlags flags = (META_MOVE_RESIZE_MOVE_ACTION | + META_MOVE_RESIZE_RESIZE_ACTION | + META_MOVE_RESIZE_STATE_CHANGED | + META_MOVE_RESIZE_UNFULLSCREEN); meta_topic (META_DEBUG_WINDOW_OPS, "Unfullscreening %s", window->desc); meta_window_config_set_is_fullscreen (window->config, FALSE); - target_rect = window->saved_rect_fullscreen; + + + if (!window->placed && + !mtk_rectangle_is_empty (&window->unconstrained_rect)) + { + place_flags |= META_PLACE_FLAG_CALCULATE; + flags |= META_MOVE_RESIZE_CONSTRAIN; + if (!window->unconstrained_rect_valid) + flags |= META_MOVE_RESIZE_RECT_INVALID; + + target_rect = window->unconstrained_rect; + } + else + { + target_rect = window->saved_rect_fullscreen; + } meta_window_frame_size_changed (window); meta_window_get_frame_rect (window, &old_frame_rect); @@ -3622,12 +3648,8 @@ meta_window_unmake_fullscreen (MetaWindow *window) window, META_SIZE_CHANGE_UNFULLSCREEN, &old_frame_rect, &old_buffer_rect); - meta_window_move_resize (window, - (META_MOVE_RESIZE_MOVE_ACTION | - META_MOVE_RESIZE_RESIZE_ACTION | - META_MOVE_RESIZE_STATE_CHANGED | - META_MOVE_RESIZE_UNFULLSCREEN), - target_rect); + meta_window_move_resize_internal (window, flags, place_flags, + target_rect); meta_display_queue_check_fullscreen (window->display); diff --git a/src/tests/meson.build b/src/tests/meson.build index 01b4b5c87a2..e4e11233a36 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -968,6 +968,7 @@ stacking_tests = [ 'window-placement', 'x11-move', 'map-maximized', + 'map-fullscreen', ] foreach stacking_test: stacking_tests diff --git a/src/tests/stacking/map-fullscreen.metatest b/src/tests/stacking/map-fullscreen.metatest new file mode 100644 index 00000000000..da920babbc4 --- /dev/null +++ b/src/tests/stacking/map-fullscreen.metatest @@ -0,0 +1,52 @@ +set_pref center-new-windows false +resize_monitor default 400 400 +new_client w wayland +new_client x x11 + +# Map a Wayland window fullscreen, and make sure it gets placed according to its +# nonfullscreen size: +# +# ------------ +# | ---- | +# | |w | | +# | ---- | +# | | +# | | +# ------------ + +create w/1 csd +resize_ignore_titlebar w/1 120 120 +fullscreen w/1 +show w/1 +wait_reconfigure w/1 +assert_size w/1 MONITOR_WIDTH MONITOR_HEIGHT +assert_position w/1 0 0 + +unfullscreen w/1 +wait_reconfigure w/1 +assert_size w/1 120 120 +assert_position w/1 18 12 + +# Map a X11 window fullscreen, and make sure it gets placed according to it's +# nonfullscreen size +# +# ------------ +# | ---- | +# | |w | | +# | ---- | +# | |x | | +# | ---- | +# ------------ + +create x/1 csd +resize_ignore_titlebar x/1 120 120 +fullscreen x/1 +show x/1 +wait_reconfigure x/1 +assert_size x/1 MONITOR_WIDTH MONITOR_HEIGHT +assert_position x/1 0 0 + +unfullscreen x/1 +wait_reconfigure x/1 +assert_size x/1 120 120 +assert_position x/1 18 132 diff --git a/src/tests/stacking/map-maximized.metatest b/src/tests/stacking/map-maximized.metatest index da28c7e802b..11ecac07ced 100644 --- a/src/tests/stacking/map-maximized.metatest +++ b/src/tests/stacking/map-maximized.metatest @@ -1,24 +1,52 @@ set_pref center-new-windows false -resize_monitor default 200 200 +resize_monitor default 400 400 new_client w wayland new_client x x11 -# Map a Wayland window maximized +# Map a Wayland window maximized, and make sure it gets placed according to its +# unmaximized size: +# +# ------------ +# | ---- | +# | |w | | +# | ---- | +# | | +# | | +# ------------ create w/1 csd -resize w/1 90 90 +resize w/1 120 120 maximize w/1 show w/1 wait_reconfigure w/1 assert_size w/1 MONITOR_WIDTH MONITOR_HEIGHT assert_position w/1 0 0 -# Map a X11 window maximized +unmaximize w/1 +wait_reconfigure w/1 +assert_size w/1 120 120 +assert_position w/1 18 12 + +# Map a X11 window maximized, and make sure it gets placed according to it's +# unmaximized size +# +# ------------ +# | ---- | +# | |w | | +# | ---- | +# | |x | | +# | ---- | +# ------------ create x/1 csd -resize x/1 90 90 +resize x/1 120 120 maximize x/1 show x/1 wait_reconfigure x/1 assert_size x/1 MONITOR_WIDTH MONITOR_HEIGHT assert_position x/1 0 0 + +unmaximize x/1 +wait_reconfigure x/1 +assert_size x/1 120 120 +assert_position x/1 18 132 diff --git a/src/wayland/meta-wayland-xdg-session-state.c b/src/wayland/meta-wayland-xdg-session-state.c index ccde2beec89..d37ad7b03f6 100644 --- a/src/wayland/meta-wayland-xdg-session-state.c +++ b/src/wayland/meta-wayland-xdg-session-state.c @@ -445,6 +445,7 @@ meta_wayland_xdg_session_state_restore_window (MetaSessionState *state, { case WINDOW_STATE_NONE: case WINDOW_STATE_FLOATING: + window->placed = TRUE; break; case WINDOW_STATE_TILED_LEFT: meta_window_tile (window, META_TILE_LEFT); @@ -461,7 +462,6 @@ meta_wayland_xdg_session_state_restore_window (MetaSessionState *state, if (toplevel_state->is_minimized) meta_window_minimize (window); - window->placed = TRUE; if (meta_is_topic_enabled (META_DEBUG_SESSION_MANAGEMENT)) { diff --git a/src/wayland/meta-wayland-xdg-shell.c b/src/wayland/meta-wayland-xdg-shell.c index 2c08efb0360..07899ced63a 100644 --- a/src/wayland/meta-wayland-xdg-shell.c +++ b/src/wayland/meta-wayland-xdg-shell.c @@ -470,7 +470,6 @@ xdg_toplevel_set_maximized (struct wl_client *client, if (!window) return; - meta_window_force_placement (window, META_PLACE_FLAG_FORCE_MOVE); meta_window_maximize (window, META_MAXIMIZE_BOTH); } diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c index 84d653b0e16..8a1b04fb345 100644 --- a/src/wayland/meta-window-wayland.c +++ b/src/wayland/meta-window-wayland.c @@ -1224,6 +1224,7 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, MtkRectangle frame_rect; MetaWindowActor *window_actor; MetaWaylandToplevelDrag *toplevel_drag; + MetaPlaceFlag place_flags = META_PLACE_FLAG_NONE; /* new_geom is in the logical pixel coordinate space, but MetaWindow wants its * rects to represent what in turn will end up on the stage, i.e. we need to @@ -1295,8 +1296,16 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, } else { - if (acked_configuration->is_fullscreen) - flags |= META_MOVE_RESIZE_CONSTRAIN; + if (!acked_configuration->is_floating) + { + flags |= META_MOVE_RESIZE_CONSTRAIN; + } + else if (!window->placed) + { + place_flags |= META_PLACE_FLAG_CALCULATE; + flags |= META_MOVE_RESIZE_CONSTRAIN; + } + if (acked_configuration->has_position) { has_position = TRUE; @@ -1306,6 +1315,13 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, } else { + if (!window->placed && + meta_window_config_is_floating (window->config)) + { + place_flags |= META_PLACE_FLAG_CALCULATE; + flags |= META_MOVE_RESIZE_CONSTRAIN; + } + if (window->placed) has_position = TRUE; } @@ -1361,7 +1377,10 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, meta_window_actor_set_tied_to_drag (window_actor, TRUE); } - meta_window_move_resize (window, flags, rect); + meta_window_move_resize_internal (window, flags, place_flags, rect); + + if (place_flags & META_PLACE_FLAG_CALCULATE) + window->placed = TRUE; } void diff --git a/src/x11/window-props.c b/src/x11/window-props.c index 3c49e3a1cb3..f83625fdad2 100644 --- a/src/x11/window-props.c +++ b/src/x11/window-props.c @@ -795,6 +795,8 @@ reload_net_wm_state (MetaWindow *window, MetaX11Display *x11_display = window->display->x11_display; MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_private (window_x11); + gboolean maximize_horizontally = FALSE; + gboolean maximize_vertically = FALSE; int i; if (!initial) @@ -821,9 +823,9 @@ reload_net_wm_state (MetaWindow *window, while (i < value->v.atom_list.n_atoms) { if (value->v.atom_list.atoms[i] == x11_display->atom__NET_WM_STATE_MAXIMIZED_HORZ) - window->maximize_horizontally_after_placement = TRUE; + maximize_horizontally = TRUE; else if (value->v.atom_list.atoms[i] == x11_display->atom__NET_WM_STATE_MAXIMIZED_VERT) - window->maximize_vertically_after_placement = TRUE; + maximize_vertically = TRUE; else if (value->v.atom_list.atoms[i] == x11_display->atom__NET_WM_STATE_HIDDEN) window->minimize_after_placement = TRUE; else if (value->v.atom_list.atoms[i] == x11_display->atom__NET_WM_STATE_MODAL) @@ -849,6 +851,10 @@ reload_net_wm_state (MetaWindow *window, ++i; } + meta_window_config_set_maximized_directions (window->config, + maximize_horizontally, + maximize_vertically); + meta_topic (META_DEBUG_X11, "Reloaded _NET_WM_STATE for %s", window->desc); -- GitLab From 436ff32d0e22bc8039ad733ed9ee4c871f153e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 23 Apr 2025 21:21:38 +0200 Subject: [PATCH 29/38] wayland/window-configuration: Add API to create from an existing one Part-of: --- .../meta-wayland-window-configuration.c | 33 +++++++++++++++++++ .../meta-wayland-window-configuration.h | 2 ++ 2 files changed, 35 insertions(+) diff --git a/src/wayland/meta-wayland-window-configuration.c b/src/wayland/meta-wayland-window-configuration.c index b8cae437a01..21f7bf67bf6 100644 --- a/src/wayland/meta-wayland-window-configuration.c +++ b/src/wayland/meta-wayland-window-configuration.c @@ -126,6 +126,39 @@ meta_wayland_window_configuration_new_empty (int bounds_width, return configuration; } +MetaWaylandWindowConfiguration * +meta_wayland_window_configuration_new_from_other (MetaWaylandWindowConfiguration *other) +{ + MetaWaylandWindowConfiguration *configuration; + + configuration = g_new0 (MetaWaylandWindowConfiguration, 1); + *configuration = (MetaWaylandWindowConfiguration) { + .ref_count = G_REF_COUNT_INIT, + .serial = ++global_serial_counter, + + .has_position = other->has_position, + .x = other->x, + .y = other->y, + .has_relative_position = other->has_relative_position, + .rel_x = other->rel_x, + .rel_y = other->rel_y, + .has_size = other->has_size, + .is_resizing = other->is_resizing, + .width = other->width, + .height = other->height, + .scale = other->scale, + .gravity = other->gravity, + .flags = other->flags, + .bounds_width = other->bounds_width, + .bounds_height = other->bounds_height, + .is_fullscreen = other->is_fullscreen, + .is_floating = other->is_floating, + .is_suspended = other->is_suspended, + }; + + return configuration; +} + MetaWaylandWindowConfiguration * meta_wayland_window_configuration_ref (MetaWaylandWindowConfiguration *configuration) { diff --git a/src/wayland/meta-wayland-window-configuration.h b/src/wayland/meta-wayland-window-configuration.h index 78b8c2bb293..ca4b4a22f8f 100644 --- a/src/wayland/meta-wayland-window-configuration.h +++ b/src/wayland/meta-wayland-window-configuration.h @@ -75,6 +75,8 @@ MetaWaylandWindowConfiguration * meta_wayland_window_configuration_new_empty (in int bounds_height, int scale); +MetaWaylandWindowConfiguration * meta_wayland_window_configuration_new_from_other (MetaWaylandWindowConfiguration *other); + MetaWaylandWindowConfiguration * meta_wayland_window_configuration_ref (MetaWaylandWindowConfiguration *configuration); void meta_wayland_window_configuration_unref (MetaWaylandWindowConfiguration *configuration); -- GitLab From 4e533a9fe3ab535a8100da76ce325d6424239cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 23 Apr 2025 21:22:14 +0200 Subject: [PATCH 30/38] wayland/window-configuration: Add API to check equivalence An equivalent configuration is one where all the outbound parameters affecting window state are the same, but not necessarily the auxiliary, such as reference count and serial number. Part-of: --- .../meta-wayland-window-configuration.c | 29 +++++++++++++++++++ .../meta-wayland-window-configuration.h | 3 ++ 2 files changed, 32 insertions(+) diff --git a/src/wayland/meta-wayland-window-configuration.c b/src/wayland/meta-wayland-window-configuration.c index 21f7bf67bf6..16b28922e42 100644 --- a/src/wayland/meta-wayland-window-configuration.c +++ b/src/wayland/meta-wayland-window-configuration.c @@ -243,3 +243,32 @@ meta_wayland_window_configuration_apply_window_config (MetaWindow return configuration; } + +gboolean +meta_wayland_window_configuration_is_equivalent (MetaWaylandWindowConfiguration *configuration, + MetaWaylandWindowConfiguration *other) +{ + g_return_val_if_fail (configuration, FALSE); + + if (!other) + return FALSE; + + return (configuration->has_position == other->has_position && + configuration->x == other->x && + configuration->y == other->y && + configuration->has_relative_position == other->has_relative_position && + configuration->rel_x == other->rel_x && + configuration->rel_y == other->rel_y && + configuration->has_size == other->has_size && + configuration->is_resizing == other->is_resizing && + configuration->width == other->width && + configuration->height == other->height && + configuration->scale == other->scale && + configuration->gravity == other->gravity && + configuration->flags == other->flags && + configuration->bounds_width == other->bounds_width && + configuration->bounds_height == other->bounds_height && + configuration->is_fullscreen == other->is_fullscreen && + configuration->is_floating == other->is_floating && + configuration->is_suspended == other->is_suspended); +} diff --git a/src/wayland/meta-wayland-window-configuration.h b/src/wayland/meta-wayland-window-configuration.h index ca4b4a22f8f..fa7f664a503 100644 --- a/src/wayland/meta-wayland-window-configuration.h +++ b/src/wayland/meta-wayland-window-configuration.h @@ -81,6 +81,9 @@ MetaWaylandWindowConfiguration * meta_wayland_window_configuration_ref (MetaWayl void meta_wayland_window_configuration_unref (MetaWaylandWindowConfiguration *configuration); +gboolean meta_wayland_window_configuration_is_equivalent (MetaWaylandWindowConfiguration *configuration, + MetaWaylandWindowConfiguration *other); + MetaWindowConfig * meta_window_config_new_from_wayland_window_configuration (MetaWindow *window, MetaWaylandWindowConfiguration *configuration); -- GitLab From de11c8232380bf860f13847600c6c700da8dc89e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 23 Apr 2025 21:26:50 +0200 Subject: [PATCH 31/38] wayland/xdg-shell: Always configure via MetaWindowWayland The initial empty config was just sent away, meaning the acknowledging machinery in meta_window_wayland_finish_move_resize() could not pick it up. Part-of: --- src/wayland/meta-wayland-xdg-shell.c | 3 ++- src/wayland/meta-window-wayland.c | 2 +- src/wayland/meta-window-wayland.h | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/wayland/meta-wayland-xdg-shell.c b/src/wayland/meta-wayland-xdg-shell.c index 07899ced63a..f1b33c0e29c 100644 --- a/src/wayland/meta-wayland-xdg-shell.c +++ b/src/wayland/meta-wayland-xdg-shell.c @@ -886,6 +886,7 @@ meta_wayland_xdg_toplevel_apply_state (MetaWaylandSurfaceRole *surface_role, if (!xdg_surface_priv->configure_sent) { + MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); g_autoptr (MetaWaylandWindowConfiguration) configuration = NULL; g_autoptr (MetaWindowConfig) window_config = NULL; int bounds_width, bounds_height, geometry_scale; @@ -927,7 +928,7 @@ meta_wayland_xdg_toplevel_apply_state (MetaWaylandSurfaceRole *surface_role, configuration, window_config); - meta_wayland_xdg_toplevel_send_configure (xdg_toplevel, configuration); + meta_window_wayland_configure (wl_window, configuration); } } diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c index 8a1b04fb345..2d950e9f5fb 100644 --- a/src/wayland/meta-window-wayland.c +++ b/src/wayland/meta-window-wayland.c @@ -190,7 +190,7 @@ meta_window_wayland_focus (MetaWindow *window, } } -static void +void meta_window_wayland_configure (MetaWindowWayland *wl_window, MetaWaylandWindowConfiguration *configuration) { diff --git a/src/wayland/meta-window-wayland.h b/src/wayland/meta-window-wayland.h index cfba8905b0c..2e22ec4f893 100644 --- a/src/wayland/meta-window-wayland.h +++ b/src/wayland/meta-window-wayland.h @@ -54,6 +54,9 @@ MetaWaylandWindowConfiguration * meta_window_wayland_peek_configuration (MetaWindowWayland *wl_window, uint32_t serial); +void meta_window_wayland_configure (MetaWindowWayland *wl_window, + MetaWaylandWindowConfiguration *configuration); + void meta_window_wayland_set_min_size (MetaWindow *window, int width, int height); -- GitLab From 500a12bba55d6a5470e9e19f0159cda9a6181582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 23 Apr 2025 21:28:47 +0200 Subject: [PATCH 32/38] window: Ignore idle resizes on unshowable windows It's pointless to try to reconstrain a window that can't yet be shown. When showing, it'll go through placement etc anyway, when necessary, so just early out to avoid pointless and potentially counter productive window management procedures. Part-of: --- src/core/window.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/window.c b/src/core/window.c index 225cb515ebe..d7fffb00f8d 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -4543,6 +4543,9 @@ meta_window_idle_move_resize (MetaWindow *window) { MetaMoveResizeFlags flags; + if (!meta_window_is_showable (window)) + return; + flags = (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION | META_MOVE_RESIZE_CONSTRAIN); -- GitLab From a44fa59e5c2fc999a6c61dae6788dbc94cb53752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 23 Apr 2025 21:37:41 +0200 Subject: [PATCH 33/38] window/wayland: Also treat tile match when checking whether resizing When two windows are resized together, only one is "grabbed", while both are effectively tied to the grab. Account for this when determining whether a window is actively being resized in the Wayland move/resize machinery. Part-of: --- src/wayland/meta-window-wayland.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c index 2d950e9f5fb..312e33ca479 100644 --- a/src/wayland/meta-window-wayland.c +++ b/src/wayland/meta-window-wayland.c @@ -1270,7 +1270,9 @@ meta_window_wayland_finish_move_resize (MetaWindow *window, is_window_being_resized = (window_drag && meta_grab_op_is_resizing (meta_window_drag_get_grab_op (window_drag)) && - meta_window_drag_get_window (window_drag) == window); + (meta_window_drag_get_window (window_drag) == window || + meta_window_drag_get_window (window_drag) == + meta_window_config_get_tile_match (window->config))); frame_rect = meta_window_config_get_rect (window->config); rect = (MtkRectangle) { -- GitLab From 6adb8fa4867a7aa52194d81ae7bb2c9f528e90ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 17 Apr 2025 22:57:09 +0200 Subject: [PATCH 34/38] tests/stacking: Add tiling test It tests that two windows tile correctly on each side, and that dragging the edge between them resizes both correctly. Part-of: --- src/tests/meson.build | 1 + src/tests/stacking/tiling.metatest | 50 ++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/tests/stacking/tiling.metatest diff --git a/src/tests/meson.build b/src/tests/meson.build index e4e11233a36..c9201640572 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -969,6 +969,7 @@ stacking_tests = [ 'x11-move', 'map-maximized', 'map-fullscreen', + 'tiling', ] foreach stacking_test: stacking_tests diff --git a/src/tests/stacking/tiling.metatest b/src/tests/stacking/tiling.metatest new file mode 100644 index 00000000000..c91c2efdf4d --- /dev/null +++ b/src/tests/stacking/tiling.metatest @@ -0,0 +1,50 @@ +new_client w wayland + +create w/1 csd +show w/1 + +create w/2 csd +show w/2 + +tile w/1 left +tile w/2 right + +wait_reconfigure w/1 +wait_reconfigure w/2 + +assert_position w/1 0 0 +assert_size w/1 MONITOR_WIDTH/2 MONITOR_HEIGHT + +assert_position w/2 MONITOR_WIDTH/2 0 +assert_size w/2 MONITOR_WIDTH/2 MONITOR_HEIGHT + +# Resize both -40 pixels horizontally + +begin_resize w/1 right +update_resize w/1 -35 0 +end_resize w/1 +wait_reconfigure w/1 +wait_reconfigure w/2 + +assert_position w/1 0 0 +assert_size w/1 MONITOR_WIDTH/2-35 MONITOR_HEIGHT + +assert_position w/2 MONITOR_WIDTH/2-35 0 +assert_size w/2 MONITOR_WIDTH/2+35 MONITOR_HEIGHT + +# Resize both +80 pixels horizontally + +begin_resize w/1 right +update_resize w/1 5 0 +update_resize w/1 10 0 +update_resize w/1 15 0 +update_resize w/1 20 0 +end_resize w/1 +wait_reconfigure w/1 +wait_reconfigure w/2 + +assert_position w/1 0 0 +assert_size w/1 MONITOR_WIDTH/2-15 MONITOR_HEIGHT + +assert_position w/2 MONITOR_WIDTH/2-15 0 +assert_size w/2 MONITOR_WIDTH/2+15 MONITOR_HEIGHT -- GitLab From 6c7565eee5aa6f1cee33c11c100f806aa421b619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 23 Apr 2025 21:52:33 +0200 Subject: [PATCH 35/38] window/wayland: Use last sent configuration to track resize changes Instead of keeping around a few fields containing "last sent size" (which isn't actually the last sent size), keep the last sent configuration around. Primarily it aims makes the MetaWindowWayland::move_resize_internal() more easy to follow, with the requirements for needing to send new configure events clearer, while making it possible to directly compare previously sent configurations with new ones. What this allows is constraining windows up-front in situations where it was previously hard, e.g. acknowledged maximized configurations when the constraints had changed. By always reconstraining acked window configurations that are directly affected by constraints, e.g. maximized/fullscreen, we can avoid a work around that queued extra move/resize's to "catch up" to changed constraints. To avoid feedback loops where buggy client doesn't respect fixed configurations, sending new configurations that are equivalent to the last sent configuration is avoided. When creating a new configuration, we didn't set the position in the configuration if the position didn't changed, to avoid unexpected window jumping. This was for the same reason done for surface state changes where we'd create a new configuration and drop the position metadata. This would however mean we couldn't use the fact whether a configuration has a position or not tied to it, which is e.g. always the case with fixed state / non-floating windows. So to make this value a bit more sensible for those situations, don't clear it when a window isn't floating. Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1627 Part-of: --- src/core/window.c | 8 - .../meta-wayland-window-configuration.c | 3 +- src/wayland/meta-window-wayland.c | 143 +++++++++++------- 3 files changed, 93 insertions(+), 61 deletions(-) diff --git a/src/core/window.c b/src/core/window.c index d7fffb00f8d..f33b06016a1 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -4268,14 +4268,6 @@ meta_window_move_resize_internal (MetaWindow *window, meta_stack_update_window_tile_matches (window->display->stack, workspace_manager->active_workspace); - - /* This is a workaround for #1627. We still don't have any tests that can - * reproduce this issue reliably and this is not a proper fix! */ - if (flags & META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE && - (result & META_MOVE_RESIZE_RESULT_MOVED || - result & META_MOVE_RESIZE_RESULT_RESIZED) && - meta_window_config_is_any_maximized (window->config)) - meta_window_queue (window, META_QUEUE_MOVE_RESIZE); } void diff --git a/src/wayland/meta-wayland-window-configuration.c b/src/wayland/meta-wayland-window-configuration.c index 16b28922e42..f9175a9af83 100644 --- a/src/wayland/meta-wayland-window-configuration.c +++ b/src/wayland/meta-wayland-window-configuration.c @@ -59,7 +59,8 @@ meta_wayland_window_configuration_new (MetaWindow *window, meta_window_config_get_position (window->config, &x, &y); if (flags & META_MOVE_RESIZE_MOVE_ACTION || x != rect.x || - y != rect.y) + y != rect.y || + !configuration->is_floating) { configuration->has_position = TRUE; configuration->x = rect.x; diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c index 312e33ca479..4cf4d9f4d30 100644 --- a/src/wayland/meta-window-wayland.c +++ b/src/wayland/meta-window-wayland.c @@ -66,13 +66,7 @@ struct _MetaWindowWayland GList *pending_configurations; gboolean has_pending_state_change; - gboolean has_last_sent_configuration; - MtkRectangle last_sent_rect; - int last_sent_rel_x; - int last_sent_rel_y; - int last_sent_geometry_scale; - MetaGravity last_sent_gravity; - + MetaWaylandWindowConfiguration *last_sent_configuration; MetaWaylandWindowConfiguration *last_acked_configuration; gboolean has_been_shown; @@ -199,36 +193,43 @@ meta_window_wayland_configure (MetaWindowWayland *wl_window, wl_window->pending_configurations = g_list_prepend (wl_window->pending_configurations, meta_wayland_window_configuration_ref (configuration)); + + g_clear_pointer (&wl_window->last_sent_configuration, + meta_wayland_window_configuration_unref); + wl_window->last_sent_configuration = + meta_wayland_window_configuration_ref (configuration); } static void surface_state_changed (MetaWindow *window) { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); + MetaWaylandWindowConfiguration *last_sent_configuration = + wl_window->last_sent_configuration; + MetaWaylandWindowConfiguration *last_acked_configuration = + wl_window->last_acked_configuration; g_autoptr (MetaWaylandWindowConfiguration) configuration = NULL; - int bounds_width; - int bounds_height; /* don't send notify when the window is being unmanaged */ if (window->unmanaging) return; - g_return_if_fail (wl_window->has_last_sent_configuration); + g_return_if_fail (last_sent_configuration); + + configuration = + meta_wayland_window_configuration_new_from_other (last_sent_configuration); + configuration->flags = META_MOVE_RESIZE_STATE_CHANGED; + configuration->is_suspended = wl_window->is_suspended; - if (!meta_window_calculate_bounds (window, &bounds_width, &bounds_height)) + if (last_acked_configuration && + last_acked_configuration->serial == last_sent_configuration->serial && + last_sent_configuration->is_floating) { - bounds_width = 0; - bounds_height = 0; + configuration->has_position = FALSE; + configuration->x = 0; + configuration->y = 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); - meta_window_wayland_configure (wl_window, configuration); } @@ -252,6 +253,43 @@ meta_window_wayland_grab_op_ended (MetaWindow *window, META_WINDOW_CLASS (meta_window_wayland_parent_class)->grab_op_ended (window, op); } +static gboolean +should_configure (MetaWindow *window, + MtkRectangle constrained_rect, + MetaMoveResizeFlags flags) +{ + MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); + MetaWaylandWindowConfiguration *last_sent_configuration = + wl_window->last_sent_configuration; + MtkRectangle frame_rect; + + frame_rect = meta_window_config_get_rect (window->config); + + /* Initial configuration, always need to configure. */ + if (!last_sent_configuration) + return TRUE; + + /* The constrained size changed from last time, also explicit, thus need to + * configure the new size. */ + if (last_sent_configuration->has_size && + (constrained_rect.width != last_sent_configuration->width || + constrained_rect.height != last_sent_configuration->height)) + return TRUE; + + /* Something wants to resize our mapped window. */ + if (meta_wayland_surface_get_buffer (wl_window->surface) && + (constrained_rect.width != frame_rect.width || + constrained_rect.height != frame_rect.height)) + return TRUE; + + /* The state was changed, or the change was explicitly marked as a configure + * request. */ + if (flags & META_MOVE_RESIZE_STATE_CHANGED) + return TRUE; + + return FALSE; +} + static void meta_window_wayland_move_resize_internal (MetaWindow *window, MtkRectangle unconstrained_rect, @@ -263,10 +301,11 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, MetaMoveResizeResultFlags *result) { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); + MetaWaylandWindowConfiguration *last_sent_configuration = + wl_window->last_sent_configuration; gboolean can_move_now = FALSE; MtkRectangle configured_rect; MtkRectangle frame_rect; - MetaTileMode tile_mode; MetaGravity gravity; int geometry_scale; int new_x; @@ -289,7 +328,6 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, * to the Wayland surface. */ geometry_scale = meta_window_wayland_get_geometry_scale (window); frame_rect = meta_window_config_get_rect (window->config); - tile_mode = meta_window_config_get_tile_mode (window->config); configured_rect.width = constrained_rect.width; configured_rect.height = constrained_rect.height; @@ -361,8 +399,9 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, case META_PLACEMENT_STATE_CONSTRAINED_PENDING: { if (flags & META_MOVE_RESIZE_PLACEMENT_CHANGED || - rel_x != wl_window->last_sent_rel_x || - rel_y != wl_window->last_sent_rel_y || + !last_sent_configuration || + rel_x != last_sent_configuration->rel_x || + rel_y != last_sent_configuration->rel_y || constrained_rect.width != frame_rect.width || constrained_rect.height != frame_rect.height) { @@ -375,14 +414,18 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, configured_rect.width, configured_rect.height, geometry_scale); - meta_window_wayland_configure (wl_window, configuration); - - wl_window->last_sent_rel_x = rel_x; - wl_window->last_sent_rel_y = rel_y; - - window->placement.state = META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED; - - can_move_now = FALSE; + if (!meta_wayland_window_configuration_is_equivalent ( + configuration, + wl_window->last_sent_configuration)) + { + meta_window_wayland_configure (wl_window, + configuration); + + window->placement.state = + META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED; + + can_move_now = FALSE; + } } else { @@ -398,20 +441,12 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, break; } } - else if (constrained_rect.width != frame_rect.width || - constrained_rect.height != frame_rect.height || - flags & META_MOVE_RESIZE_STATE_CHANGED) + else if (should_configure (window, unconstrained_rect, flags)) { g_autoptr (MetaWaylandWindowConfiguration) configuration = NULL; int bounds_width; int bounds_height; - if (!meta_wayland_surface_get_buffer (wl_window->surface) && - !meta_window_is_maximized (window) && - tile_mode == META_TILE_NONE && - !meta_window_is_fullscreen (window)) - return; - if (!meta_window_calculate_bounds (window, &bounds_width, &bounds_height)) @@ -427,8 +462,13 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, geometry_scale, flags, gravity); - meta_window_wayland_configure (wl_window, configuration); - can_move_now = FALSE; + if (!meta_wayland_window_configuration_is_equivalent ( + configuration, + wl_window->last_sent_configuration)) + { + meta_window_wayland_configure (wl_window, configuration); + can_move_now = FALSE; + } } else { @@ -436,11 +476,6 @@ meta_window_wayland_move_resize_internal (MetaWindow *window, } } - wl_window->has_last_sent_configuration = TRUE; - wl_window->last_sent_rect = configured_rect; - wl_window->last_sent_geometry_scale = geometry_scale; - wl_window->last_sent_gravity = gravity; - if (can_move_now) { new_x = constrained_rect.x; @@ -932,6 +967,8 @@ meta_window_wayland_finalize (GObject *object) g_clear_pointer (&wl_window->last_acked_configuration, meta_wayland_window_configuration_unref); + g_clear_pointer (&wl_window->last_sent_configuration, + meta_wayland_window_configuration_unref); g_list_free_full (wl_window->pending_configurations, (GDestroyNotify) meta_wayland_window_configuration_unref); @@ -1139,13 +1176,15 @@ meta_window_wayland_is_resize (MetaWindowWayland *wl_window, int width, int height) { + MetaWaylandWindowConfiguration *last_sent_configuration = + wl_window->last_sent_configuration; int old_width; int old_height; if (wl_window->pending_configurations) { - old_width = wl_window->last_sent_rect.width; - old_height = wl_window->last_sent_rect.height; + old_width = last_sent_configuration->width; + old_height = last_sent_configuration->height; } else { @@ -1153,7 +1192,7 @@ meta_window_wayland_is_resize (MetaWindowWayland *wl_window, meta_window_config_get_size (window->config, &old_width, &old_height); } - return !wl_window->has_last_sent_configuration || + return !last_sent_configuration || old_width != width || old_height != height; } -- GitLab From 6863c157cfbe2cc95fc5ade71527c06b98be1314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 24 Apr 2025 11:53:39 +0200 Subject: [PATCH 36/38] tests: Add interactive move tests This test checks that configuration from state changes (in this case 'active' (or focused)) doesn't negatively affect the window position after an interactive move. To be specific, this ensures that window configurations don't incorrectly contain stale positions when the position changed, but no new configuration was sent to reflect that. Part-of: --- src/tests/meson.build | 1 + src/tests/stacking/interactive-move.metatest | 26 ++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/tests/stacking/interactive-move.metatest diff --git a/src/tests/meson.build b/src/tests/meson.build index c9201640572..1d1380cc700 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -970,6 +970,7 @@ stacking_tests = [ 'map-maximized', 'map-fullscreen', 'tiling', + 'interactive-move', ] foreach stacking_test: stacking_tests diff --git a/src/tests/stacking/interactive-move.metatest b/src/tests/stacking/interactive-move.metatest new file mode 100644 index 00000000000..1eb8ffbe249 --- /dev/null +++ b/src/tests/stacking/interactive-move.metatest @@ -0,0 +1,26 @@ +new_client w wayland + +# +# This test case ensures that window state configuration changes after an +# interactive move doesn't cause unexpected window movement. +# + +create w/1 +show w/1 +wait +move w/1 0 10 +maximize w/1 +unmaximize w/1 +wait_reconfigure w/1 +assert_position w/1 0 10 + +move w/1 20 20 +assert_position w/1 20 20 + +create w/2 +show w/2 +wait + +assert_focused w/2 + +assert_position w/1 20 20 -- GitLab From 38923b8c2790e6bff14212fdc238acd543943779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 24 Apr 2025 13:03:00 +0200 Subject: [PATCH 37/38] tests: Add test checking we place windows on the right monitor It also ensures we un(maximize/fullscreen) accordingly as well. The expected behavior is that mapping places the window on the "current" monitor (where current means where the pointer is at), and that making an initially maximized or fullscreen window floating doesn't change its monitor. Part-of: --- src/tests/meson.build | 1 + src/tests/stacking/map-on-monitor.metatest | 109 +++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 src/tests/stacking/map-on-monitor.metatest diff --git a/src/tests/meson.build b/src/tests/meson.build index 1d1380cc700..ea6962c2c42 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -971,6 +971,7 @@ stacking_tests = [ 'map-fullscreen', 'tiling', 'interactive-move', + 'map-on-monitor', ] foreach stacking_test: stacking_tests diff --git a/src/tests/stacking/map-on-monitor.metatest b/src/tests/stacking/map-on-monitor.metatest new file mode 100644 index 00000000000..7c048193aff --- /dev/null +++ b/src/tests/stacking/map-on-monitor.metatest @@ -0,0 +1,109 @@ +resize_monitor default 640 480 +add_monitor secondary 800 600 + +move_cursor_to 700 200 + +new_client w wayland +new_client x x11 + +# +# Wayland +# + +# Map floating on secondary monitor + +create w/1 csd +resize w/1 100 100 +show w/1 +assert_position w/1 990 250 +assert_size w/1 100 100 +destroy w/1 + +# Map maximized on secondary monitor, move cursor, then unmaximize + +create w/2 csd +resize w/2 100 100 +maximize w/2 +show w/2 +assert_position w/2 640 0 +assert_size w/2 800 600 + +move_cursor_to 10 10 + +unmaximize w/2 +wait_reconfigure w/2 +assert_position w/2 990 250 +assert_size w/2 100 100 +destroy w/2 + +# Map fullscreen on secondary monitor, then unfullscreen + +move_cursor_to 700 200 + +create w/3 csd +resize_ignore_titlebar w/3 100 100 +fullscreen w/3 +show w/3 +assert_position w/3 640 0 +assert_size w/3 800 600 + +move_cursor_to 10 10 + +unfullscreen w/3 +wait_reconfigure w/3 +assert_position w/3 990 250 +assert_size w/3 100 100 +destroy w/3 + +# +# X11 +# + +# Map floating on secondary monitor + +move_cursor_to 700 200 + +create x/1 csd +resize x/1 100 100 +show x/1 +assert_position x/1 990 250 +assert_size x/1 100 100 +destroy x/1 + +# Map maximized on secondary monitor, then unmaximize + +move_cursor_to 700 200 + +create x/2 csd +resize x/2 100 100 +maximize x/2 +show x/2 +assert_position x/2 640 0 +assert_size x/2 800 600 + +move_cursor_to 10 10 + +unmaximize x/2 +wait_reconfigure x/2 +assert_position x/2 990 250 +assert_size x/2 100 100 +destroy x/2 + +# Map fullscreen on secondary monitor, then unfullscreen + +move_cursor_to 700 200 + +create x/3 csd +resize_ignore_titlebar x/3 100 100 +fullscreen x/3 +show x/3 +assert_position x/3 640 0 +assert_size x/3 800 600 + +move_cursor_to 10 10 + +unfullscreen x/3 +wait_reconfigure x/3 +assert_position x/3 990 250 +assert_size x/3 100 100 +destroy x/3 -- GitLab From 343db06b24d2dd5f9710a94b588089302cfd4fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 29 Apr 2025 00:00:45 +0200 Subject: [PATCH 38/38] window: Fix variable naming of property get/set function Add a couple of missing white spaces while at it. Part-of: --- src/core/window.c | 82 +++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/core/window.c b/src/core/window.c index f33b06016a1..776f08f288c 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -377,25 +377,25 @@ meta_window_finalize (GObject *object) } static void -meta_window_get_property(GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) +meta_window_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) { - MetaWindow *win = META_WINDOW (object); - MetaWindowPrivate *priv = meta_window_get_instance_private (win); - MetaWindowConfig *config = win->config; + MetaWindow *window = META_WINDOW (object); + MetaWindowPrivate *priv = meta_window_get_instance_private (window); + MetaWindowConfig *config = window->config; switch (prop_id) { case PROP_TITLE: - g_value_set_string (value, win->title); + g_value_set_string (value, window->title); break; case PROP_DECORATED: - g_value_set_boolean (value, win->decorated); + g_value_set_boolean (value, window->decorated); break; case PROP_FULLSCREEN: - g_value_set_boolean (value, meta_window_is_fullscreen (win)); + g_value_set_boolean (value, meta_window_is_fullscreen (window)); break; case PROP_MAXIMIZED_HORIZONTALLY: g_value_set_boolean (value, @@ -406,79 +406,79 @@ meta_window_get_property(GObject *object, meta_window_config_is_maximized_vertically (config)); break; case PROP_MINIMIZED: - g_value_set_boolean (value, win->minimized); + g_value_set_boolean (value, window->minimized); break; case PROP_WINDOW_TYPE: - g_value_set_enum (value, win->type); + g_value_set_enum (value, window->type); break; case PROP_USER_TIME: - g_value_set_uint (value, win->net_wm_user_time); + g_value_set_uint (value, window->net_wm_user_time); break; case PROP_DEMANDS_ATTENTION: - g_value_set_boolean (value, win->wm_state_demands_attention); + g_value_set_boolean (value, window->wm_state_demands_attention); break; case PROP_URGENT: - g_value_set_boolean (value, win->urgent); + g_value_set_boolean (value, window->urgent); break; case PROP_SKIP_TASKBAR: - g_value_set_boolean (value, win->skip_taskbar); + g_value_set_boolean (value, window->skip_taskbar); break; case PROP_MUTTER_HINTS: - g_value_set_string (value, win->mutter_hints); + g_value_set_string (value, window->mutter_hints); break; case PROP_APPEARS_FOCUSED: - g_value_set_boolean (value, win->appears_focused); + g_value_set_boolean (value, window->appears_focused); break; case PROP_WM_CLASS: - g_value_set_string (value, win->res_class); + g_value_set_string (value, window->res_class); break; case PROP_RESIZEABLE: - g_value_set_boolean (value, win->has_resize_func); + g_value_set_boolean (value, window->has_resize_func); break; case PROP_ABOVE: - g_value_set_boolean (value, win->wm_state_above); + g_value_set_boolean (value, window->wm_state_above); break; case PROP_GTK_APPLICATION_ID: - g_value_set_string (value, win->gtk_application_id); + g_value_set_string (value, window->gtk_application_id); break; case PROP_GTK_UNIQUE_BUS_NAME: - g_value_set_string (value, win->gtk_unique_bus_name); + g_value_set_string (value, window->gtk_unique_bus_name); break; case PROP_GTK_APPLICATION_OBJECT_PATH: - g_value_set_string (value, win->gtk_application_object_path); + g_value_set_string (value, window->gtk_application_object_path); break; case PROP_GTK_WINDOW_OBJECT_PATH: - g_value_set_string (value, win->gtk_window_object_path); + g_value_set_string (value, window->gtk_window_object_path); break; case PROP_GTK_APP_MENU_OBJECT_PATH: - g_value_set_string (value, win->gtk_app_menu_object_path); + g_value_set_string (value, window->gtk_app_menu_object_path); break; case PROP_GTK_MENUBAR_OBJECT_PATH: - g_value_set_string (value, win->gtk_menubar_object_path); + g_value_set_string (value, window->gtk_menubar_object_path); break; case PROP_ON_ALL_WORKSPACES: - g_value_set_boolean (value, win->on_all_workspaces); + g_value_set_boolean (value, window->on_all_workspaces); break; case PROP_IS_ALIVE: - g_value_set_boolean (value, win->is_alive); + g_value_set_boolean (value, window->is_alive); break; case PROP_DISPLAY: - g_value_set_object (value, win->display); + g_value_set_object (value, window->display); break; case PROP_EFFECT: - g_value_set_int (value, win->pending_compositor_effect); + g_value_set_int (value, window->pending_compositor_effect); break; case PROP_SUSPEND_STATE: g_value_set_enum (value, priv->suspend_state); break; case PROP_MAPPED: - g_value_set_boolean (value, win->mapped); + g_value_set_boolean (value, window->mapped); break; case PROP_MAIN_MONITOR: - g_value_set_object (value, win->monitor); + g_value_set_object (value, window->monitor); break; case PROP_TAG: - g_value_set_string (value, win->tag); + g_value_set_string (value, window->tag); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -487,20 +487,20 @@ meta_window_get_property(GObject *object, } static void -meta_window_set_property(GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) +meta_window_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) { - MetaWindow *win = META_WINDOW (object); + MetaWindow *window = META_WINDOW (object); switch (prop_id) { case PROP_DISPLAY: - win->display = g_value_get_object (value); + window->display = g_value_get_object (value); break; case PROP_EFFECT: - win->pending_compositor_effect = g_value_get_int (value); + window->pending_compositor_effect = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -- GitLab