diff --git a/src/core/window.c b/src/core/window.c
index d54d73d40..1411d355c 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -168,6 +168,8 @@ static MetaWindow * meta_window_find_tile_match (MetaWindow *window,
MetaTileMode mode);
static void update_edge_constraints (MetaWindow *window);
+static void set_hidden_suspended_state (MetaWindow *window);
+
static void initable_iface_init (GInitableIface *initable_iface);
typedef struct _MetaWindowPrivate
@@ -713,9 +715,6 @@ meta_window_class_init (MetaWindowClass *klass)
static void
meta_window_init (MetaWindow *window)
{
- MetaWindowPrivate *priv = meta_window_get_instance_private (window);
-
- priv->suspend_state = META_WINDOW_SUSPEND_STATE_SUSPENDED;
window->stamp = next_window_stamp++;
meta_prefs_add_listener (prefs_changed_callback, window);
window->is_alive = TRUE;
@@ -991,6 +990,7 @@ static void
meta_window_constructed (GObject *object)
{
MetaWindow *window = META_WINDOW (object);
+ MetaWindowPrivate *priv = meta_window_get_instance_private (window);
MetaDisplay *display = window->display;
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
@@ -1343,6 +1343,11 @@ meta_window_constructed (GObject *object)
!window->initially_iconic)
unminimize_window_and_all_transient_parents (window);
+ /* There is a slim chance we'll hit time out before a extremely slow client
+ * managed to become active, but unlikely enough. */
+ priv->suspend_state = META_WINDOW_SUSPEND_STATE_HIDDEN;
+ set_hidden_suspended_state (window);
+
window->constructing = FALSE;
}
@@ -2164,6 +2169,19 @@ enter_suspend_state_cb (gpointer user_data)
return G_SOURCE_REMOVE;
}
+static void
+set_hidden_suspended_state (MetaWindow *window)
+{
+ MetaWindowPrivate *priv = meta_window_get_instance_private (window);
+
+ priv->suspend_state = META_WINDOW_SUSPEND_STATE_HIDDEN;
+ g_return_if_fail (!priv->suspend_timoeut_id);
+ priv->suspend_timoeut_id =
+ g_timeout_add_seconds (SUSPEND_HIDDEN_TIMEOUT_S,
+ enter_suspend_state_cb,
+ window);
+}
+
static void
update_suspend_state (MetaWindow *window)
{
@@ -2181,13 +2199,8 @@ update_suspend_state (MetaWindow *window)
}
else if (priv->suspend_state == META_WINDOW_SUSPEND_STATE_ACTIVE)
{
- priv->suspend_state = META_WINDOW_SUSPEND_STATE_HIDDEN;
+ set_hidden_suspended_state (window);
g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_SUSPEND_STATE]);
- g_return_if_fail (!priv->suspend_timoeut_id);
- priv->suspend_timoeut_id =
- g_timeout_add_seconds (SUSPEND_HIDDEN_TIMEOUT_S,
- enter_suspend_state_cb,
- window);
}
}
diff --git a/src/tests/wayland-test-clients/meson.build b/src/tests/wayland-test-clients/meson.build
index 38ac799d0..1cdf1e62e 100644
--- a/src/tests/wayland-test-clients/meson.build
+++ b/src/tests/wayland-test-clients/meson.build
@@ -81,6 +81,9 @@ wayland_test_clients = [
{
'name': 'xdg-toplevel-bounds',
},
+ {
+ 'name': 'xdg-toplevel-suspended',
+ },
{
'name': 'ycbcr',
},
diff --git a/src/tests/wayland-test-clients/xdg-toplevel-suspended.c b/src/tests/wayland-test-clients/xdg-toplevel-suspended.c
new file mode 100644
index 000000000..4e58d2674
--- /dev/null
+++ b/src/tests/wayland-test-clients/xdg-toplevel-suspended.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2024 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+
+#include "config.h"
+
+#include
+#include
+
+#include "wayland-test-client-utils.h"
+
+enum
+{
+ XDG_TOPLEVEL_SUSPENDED_COMMAND_NEXT_WORKSPACE = 0,
+ XDG_TOPLEVEL_SUSPENDED_COMMAND_PREV_WORKSPACE = 1,
+ XDG_TOPLEVEL_SUSPENDED_COMMAND_ACTIVATE_WINDOW = 2,
+};
+
+static void
+wait_for_state (WaylandSurface *surface,
+ enum xdg_toplevel_state state)
+{
+ while (!wayland_surface_has_state (surface, state))
+ wayland_display_dispatch (surface->display);
+}
+
+static void
+wait_for_no_state (WaylandSurface *surface,
+ enum xdg_toplevel_state state)
+{
+ while (wayland_surface_has_state (surface, state))
+ wayland_display_dispatch (surface->display);
+}
+
+static void
+test_floating (WaylandDisplay *display)
+{
+ g_autoptr (WaylandSurface) surface = NULL;
+
+ g_debug ("Testing suspended state when mapping floating");
+
+ surface = wayland_surface_new (display, __func__, 100, 100, 0xffffffff);
+ wl_surface_commit (surface->wl_surface);
+
+ wait_for_window_shown (display, surface->wl_surface);
+ g_assert_false (wayland_surface_has_state (surface,
+ XDG_TOPLEVEL_STATE_SUSPENDED));
+}
+
+static void
+test_maximized (WaylandDisplay *display)
+{
+ g_autoptr (WaylandSurface) surface = NULL;
+
+ g_debug ("Testing suspended state when mapping maximized");
+
+ surface = wayland_surface_new (display, __func__, 100, 100, 0xffffffff);
+ xdg_toplevel_set_maximized (surface->xdg_toplevel);
+ wl_surface_commit (surface->wl_surface);
+
+ wait_for_window_shown (display, surface->wl_surface);
+ g_assert_false (wayland_surface_has_state (surface,
+ XDG_TOPLEVEL_STATE_SUSPENDED));
+}
+
+static void
+test_minimized (WaylandDisplay *display)
+{
+ g_autoptr (WaylandSurface) surface = NULL;
+
+ g_debug ("Testing suspended state when mapping minimized");
+
+ surface = wayland_surface_new (display, __func__, 100, 100, 0xffffffff);
+ wl_surface_commit (surface->wl_surface);
+
+ wait_for_window_shown (display, surface->wl_surface);
+ g_assert_false (wayland_surface_has_state (surface,
+ XDG_TOPLEVEL_STATE_SUSPENDED));
+
+ xdg_toplevel_set_minimized (surface->xdg_toplevel);
+ wait_for_state (surface, XDG_TOPLEVEL_STATE_SUSPENDED);
+}
+
+static void
+test_workspace_changes (WaylandDisplay *display)
+{
+ g_autoptr (WaylandSurface) surface = NULL;
+
+ g_debug ("Testing suspended state when changing workspace");
+
+ surface = wayland_surface_new (display, __func__, 100, 100, 0xffffffff);
+ wl_surface_commit (surface->wl_surface);
+
+ wait_for_window_shown (display, surface->wl_surface);
+ g_assert_false (wayland_surface_has_state (surface,
+ XDG_TOPLEVEL_STATE_SUSPENDED));
+
+
+ test_driver_sync_point (display->test_driver,
+ XDG_TOPLEVEL_SUSPENDED_COMMAND_NEXT_WORKSPACE,
+ NULL);
+
+ wait_for_state (surface, XDG_TOPLEVEL_STATE_SUSPENDED);
+
+ test_driver_sync_point (display->test_driver,
+ XDG_TOPLEVEL_SUSPENDED_COMMAND_PREV_WORKSPACE,
+ NULL);
+
+ wait_for_no_state (surface, XDG_TOPLEVEL_STATE_SUSPENDED);
+}
+
+static void
+test_obstructed (WaylandDisplay *display)
+{
+ g_autoptr (WaylandSurface) surface = NULL;
+ g_autoptr (WaylandSurface) cover_surface = NULL;
+
+ g_debug ("Testing suspended state when obstructed");
+
+ surface = wayland_surface_new (display, __func__,
+ 100, 100, 0xffffffff);
+ wl_surface_commit (surface->wl_surface);
+
+ wait_for_window_shown (display, surface->wl_surface);
+ g_assert_false (wayland_surface_has_state (surface,
+ XDG_TOPLEVEL_STATE_SUSPENDED));
+
+ cover_surface = wayland_surface_new (display, "obstruction",
+ 100, 100, 0xffffffff);
+ xdg_toplevel_set_maximized (cover_surface->xdg_toplevel);
+ wl_surface_commit (cover_surface->wl_surface);
+
+ wait_for_window_shown (display, cover_surface->wl_surface);
+ test_driver_sync_point (display->test_driver,
+ XDG_TOPLEVEL_SUSPENDED_COMMAND_ACTIVATE_WINDOW,
+ cover_surface->wl_surface);
+
+ wait_for_state (surface, XDG_TOPLEVEL_STATE_SUSPENDED);
+
+ g_clear_object (&cover_surface);
+
+ wait_for_no_state (surface, XDG_TOPLEVEL_STATE_SUSPENDED);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ g_autoptr (WaylandDisplay) display = NULL;
+ g_autoptr (WaylandSurface) surface = NULL;
+
+ display = wayland_display_new (WAYLAND_DISPLAY_CAPABILITY_TEST_DRIVER |
+ WAYLAND_DISPLAY_CAPABILITY_XDG_SHELL_V6);
+
+ test_floating (display);
+ test_maximized (display);
+ test_minimized (display);
+ test_workspace_changes (display);
+ test_obstructed (display);
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/tests/wayland-unit-tests.c b/src/tests/wayland-unit-tests.c
index 692b723df..681968da2 100644
--- a/src/tests/wayland-unit-tests.c
+++ b/src/tests/wayland-unit-tests.c
@@ -868,6 +868,73 @@ toplevel_show_states (void)
meta_wayland_test_client_finish (wayland_test_client);
}
+enum
+{
+ XDG_TOPLEVEL_SUSPENDED_COMMAND_NEXT_WORKSPACE = 0,
+ XDG_TOPLEVEL_SUSPENDED_COMMAND_PREV_WORKSPACE = 1,
+ XDG_TOPLEVEL_SUSPENDED_COMMAND_ACTIVATE_WINDOW = 2,
+};
+
+static void
+on_toplevel_suspended_sync_point (MetaWaylandTestDriver *test_driver,
+ unsigned int sequence,
+ struct wl_resource *surface_resource,
+ struct wl_client *wl_client)
+{
+ MetaDisplay *display = meta_context_get_display (test_context);
+ MetaWorkspaceManager *workspace_manager =
+ meta_display_get_workspace_manager (display);
+ MetaWorkspace *current_workspace;
+ int index;
+ MetaWorkspace *workspace;
+ MetaWaylandSurface *surface;
+ uint32_t now_ms;
+
+ current_workspace =
+ meta_workspace_manager_get_active_workspace (workspace_manager);
+ index = meta_workspace_index (current_workspace);
+ switch (sequence)
+ {
+ case XDG_TOPLEVEL_SUSPENDED_COMMAND_NEXT_WORKSPACE:
+ workspace =
+ meta_workspace_manager_get_workspace_by_index (workspace_manager,
+ index + 1);
+ now_ms = meta_display_get_current_time_roundtrip (display);
+ meta_workspace_activate (workspace, now_ms);
+ break;
+ case XDG_TOPLEVEL_SUSPENDED_COMMAND_PREV_WORKSPACE:
+ workspace =
+ meta_workspace_manager_get_workspace_by_index (workspace_manager,
+ index - 1);
+ now_ms = meta_display_get_current_time_roundtrip (display);
+ meta_workspace_activate (workspace, now_ms);
+ break;
+ case XDG_TOPLEVEL_SUSPENDED_COMMAND_ACTIVATE_WINDOW:
+ surface = wl_resource_get_user_data (surface_resource);
+ now_ms = meta_display_get_current_time_roundtrip (display);
+ meta_window_activate (meta_wayland_surface_get_window (surface), now_ms);
+ break;
+ }
+}
+
+static void
+toplevel_suspended (void)
+{
+ MetaWaylandTestClient *wayland_test_client;
+ gulong sync_point_id;
+
+ sync_point_id =
+ g_signal_connect (test_driver, "sync-point",
+ G_CALLBACK (on_toplevel_suspended_sync_point),
+ NULL);
+
+ wayland_test_client =
+ meta_wayland_test_client_new (test_context, "xdg-toplevel-suspended");
+ meta_wayland_test_client_finish (wayland_test_client);
+
+ g_signal_handler_disconnect (test_driver, sync_point_id);
+}
+
static void
on_before_tests (void)
{
@@ -947,6 +1014,8 @@ init_tests (void)
xdg_foreign_set_parent_of);
g_test_add_func ("/wayland/toplevel/show-states",
toplevel_show_states);
+ g_test_add_func ("/wayland/toplevel/suspended",
+ toplevel_suspended);
}
int