From b14301e520c15fc2c5166bfbed06e14d03bbe878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 17 Mar 2026 16:50:04 +0100 Subject: [PATCH 1/4] tests/test-runner: Add 'remove_monitor' command This allows removing any added monitor, or the monitor added by default. --- src/tests/test-runner.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/tests/test-runner.c b/src/tests/test-runner.c index e036b16bd6..6a8375144d 100644 --- a/src/tests/test-runner.c +++ b/src/tests/test-runner.c @@ -2324,6 +2324,18 @@ test_case_do (TestCase *test, g_hash_table_insert (test->virtual_monitors, g_strdup (argv[1]), monitor); } + else if (strcmp (argv[0], "remove_monitor") == 0) + { + MetaBackend *backend = meta_context_get_backend (test->context); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + + if (argc != 2) + BAD_COMMAND ("usage: %s ", argv[0]); + + g_hash_table_remove (test->virtual_monitors, g_strdup (argv[1])); + meta_monitor_manager_reload (monitor_manager); + } else if (strcmp (argv[0], "set_monitor_order") == 0) { MetaBackend *backend = meta_context_get_backend (test->context); -- 2.51.0 From b4f6e1b3fac0440216c319ec4936df8cd4108f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 17 Mar 2026 16:50:44 +0100 Subject: [PATCH 2/4] tests/test-runner: Handle sanity checking while headless When headless, monitors will not have a current monitor, as there is none. Handle this. --- src/backends/meta-monitor-manager-private.h | 1 + src/tests/test-runner.c | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/backends/meta-monitor-manager-private.h b/src/backends/meta-monitor-manager-private.h index e219c95aaf..522730c904 100644 --- a/src/backends/meta-monitor-manager-private.h +++ b/src/backends/meta-monitor-manager-private.h @@ -353,6 +353,7 @@ void meta_monitor_manager_update_logical_state_derived (MetaMonito META_EXPORT_TEST void meta_monitor_manager_lid_is_closed_changed (MetaMonitorManager *manager); +META_EXPORT_TEST gboolean meta_monitor_manager_is_headless (MetaMonitorManager *manager); float meta_monitor_manager_calculate_monitor_mode_scale (MetaMonitorManager *manager, diff --git a/src/tests/test-runner.c b/src/tests/test-runner.c index 6a8375144d..551ac15357 100644 --- a/src/tests/test-runner.c +++ b/src/tests/test-runner.c @@ -3183,11 +3183,24 @@ sanity_check_monitor (MetaWindow *window) { if (!meta_window_is_hidden (window)) { - MtkRectangle rect; + MetaContext *context = meta_display_get_context (window->display); + MetaBackend *backend = meta_context_get_backend (context); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); - g_assert_nonnull (window->monitor); - rect = meta_window_config_get_rect (window->config); - g_assert_true (mtk_rectangle_overlap (&rect, &window->monitor->rect)); + if (meta_monitor_manager_is_headless (monitor_manager)) + { + g_assert_null (window->monitor); + } + else + { + MtkRectangle rect; + + g_assert_nonnull (window->monitor); + + rect = meta_window_config_get_rect (window->config); + g_assert_true (mtk_rectangle_overlap (&rect, &window->monitor->rect)); + } } } -- 2.51.0 From dd0811b6a1576df8d1d8fd9c0fdb0381fec805e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 17 Apr 2026 09:37:43 +0200 Subject: [PATCH 3/4] window: Bail proper unmaximizing when headless We don't have any information to handle placing a window when unmaximizing and headless, so just place it at 0,0. --- src/core/window.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/core/window.c b/src/core/window.c index 8f44f60d22..2b2ad8b0ac 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -3443,6 +3443,7 @@ meta_window_set_unmaximize_flags (MetaWindow *window, gboolean has_desired_rect = FALSE; MtkRectangle target_rect; MtkRectangle work_area; + gboolean has_work_area = FALSE; MtkRectangle old_frame_rect, old_buffer_rect; gboolean has_target_size; MetaPlaceFlag place_flags = META_PLACE_FLAG_NONE; @@ -3452,7 +3453,11 @@ meta_window_set_unmaximize_flags (MetaWindow *window, reset_pending_auto_maximize (window); - meta_window_get_work_area_current_monitor (window, &work_area); + if (window->monitor) + { + meta_window_get_work_area_current_monitor (window, &work_area); + has_work_area = TRUE; + } meta_window_get_frame_rect (window, &old_frame_rect); meta_window_get_buffer_rect (window, &old_buffer_rect); @@ -3508,6 +3513,7 @@ meta_window_set_unmaximize_flags (MetaWindow *window, */ if (unmaximize_horizontally && unmaximize_vertically && has_desired_rect && + has_work_area && desired_rect.width * desired_rect.height > work_area.width * work_area.height * MAX_UNMAXIMIZED_WINDOW_AREA) { @@ -3572,7 +3578,7 @@ meta_window_set_unmaximize_flags (MetaWindow *window, meta_window_recalc_features (window); set_net_wm_state (window); - if (!window->monitor->in_fullscreen) + if (window->monitor && !window->monitor->in_fullscreen) meta_display_queue_check_fullscreen (window->display); } -- 2.51.0 From 0c410fe7c31a9cbc1c9c2a5a1f115f509a97332f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 16 Apr 2026 23:38:28 +0200 Subject: [PATCH 4/4] tests: Add test for unmaximizing while headless A test is added that verifies the behavior when unmaximizing while no monitor is connected. The 'dispatch' test runner command had to be adapted to handle not getting any frame clock dispatches, since there are no frame CRTC associated frame clocks to dispatch from. --- src/tests/meson.build | 1 + .../stacking/unmaximize-headless.metatest | 61 +++++++++++++++++++ src/tests/test-runner.c | 32 ++++++---- 3 files changed, 83 insertions(+), 11 deletions(-) create mode 100644 src/tests/stacking/unmaximize-headless.metatest diff --git a/src/tests/meson.build b/src/tests/meson.build index 8c3773033f..64ea5303b1 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -1181,6 +1181,7 @@ stacking_tests = [ 'partially-maximized', 'move-to-monitor', 'keyboard-resize', + 'unmaximize-headless', ] foreach stacking_test: stacking_tests diff --git a/src/tests/stacking/unmaximize-headless.metatest b/src/tests/stacking/unmaximize-headless.metatest new file mode 100644 index 0000000000..0a738c35d0 --- /dev/null +++ b/src/tests/stacking/unmaximize-headless.metatest @@ -0,0 +1,61 @@ +set_pref center-new-windows true + +# Test unmaximizing when there is no monitor to place on + +new_client w wayland +create w/1 csd +resize w/1 200 200 +maximize w/1 +show w/1 +wait + +remove_monitor default + +unmaximize w/1 +wait_reconfigure w/1 + +add_monitor default 800 600 +wait_reconfigure w/1 +assert_size w/1 200 200 +# Incorrect placement expected. +#assert_position w/1 300 200 + +remove_monitor default + +maximize w/1 +wait_reconfigure w/1 + +add_monitor default 1024 768 +wait_reconfigure w/1 +assert_size w/1 1024 768 +assert_position w/1 0 0 + +# Same test with an X11 client + +new_client x x11 +create x/1 csd +resize x/1 200 200 +maximize x/1 +show x/1 +wait + +remove_monitor default + +unmaximize x/1 +wait_reconfigure x/1 + +add_monitor default 800 600 +wait_reconfigure x/1 +assert_size x/1 200 200 +# Incorrect placement expected. +#assert_position x/1 300 200 + +remove_monitor default + +maximize x/1 +wait_reconfigure x/1 + +add_monitor default 1024 768 +wait_reconfigure x/1 +assert_size x/1 1024 768 +assert_position x/1 0 0 diff --git a/src/tests/test-runner.c b/src/tests/test-runner.c index 551ac15357..235575660f 100644 --- a/src/tests/test-runner.c +++ b/src/tests/test-runner.c @@ -163,19 +163,29 @@ test_case_dispatch (TestCase *test, MetaDisplay *display = meta_context_get_display (test->context); MetaCompositor *compositor = meta_display_get_compositor (display); MetaLaters *laters = meta_compositor_get_laters (compositor); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); - /* Wait until we've done any outstanding queued up work. - * Though we add this as BEFORE_REDRAW, the iteration that runs the - * BEFORE_REDRAW idles will proceed on and do the redraw, so we're - * waiting until after *all* frame processing. - */ - meta_laters_add (laters, META_LATER_BEFORE_REDRAW, - test_case_loop_quit, - test, - NULL); + if (meta_monitor_manager_is_headless (monitor_manager)) + { + while (g_main_context_iteration (NULL, FALSE)) + ; + } + else + { + /* Wait until we've done any outstanding queued up work. + * Though we add this as BEFORE_REDRAW, the iteration that runs the + * BEFORE_REDRAW idles will proceed on and do the redraw, so we're + * waiting until after *all* frame processing. + */ + meta_laters_add (laters, META_LATER_BEFORE_REDRAW, + test_case_loop_quit, + test, + NULL); - clutter_stage_schedule_update (CLUTTER_STAGE (stage)); - g_main_loop_run (test->loop); + clutter_stage_schedule_update (CLUTTER_STAGE (stage)); + g_main_loop_run (test->loop); + } return TRUE; } -- 2.51.0