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