diff --git a/src/tests/meson.build b/src/tests/meson.build index 59385dd8d..889c3b019 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -630,6 +630,8 @@ wayland_test_cases = [ test_client_executables.get('xdg-apply-limits'), test_client_executables.get('xdg-foreign'), test_client_executables.get('xdg-toplevel-bounds'), + test_client_executables.get('xdg-session-management'), + test_client_executables.get('xdg-session-management-replace'), test_client_executables.get('ycbcr'), ], }, diff --git a/src/tests/wayland-test-clients/meson.build b/src/tests/wayland-test-clients/meson.build index 7a1846ea3..708c541df 100644 --- a/src/tests/wayland-test-clients/meson.build +++ b/src/tests/wayland-test-clients/meson.build @@ -81,6 +81,12 @@ wayland_test_clients = [ { 'name': 'xdg-foreign', }, + { + 'name': 'xdg-session-management', + }, + { + 'name': 'xdg-session-management-replace', + }, { 'name': 'xdg-toplevel-bounds', }, diff --git a/src/tests/wayland-test-clients/xdg-session-management-replace.c b/src/tests/wayland-test-clients/xdg-session-management-replace.c new file mode 100644 index 000000000..89cd87d1a --- /dev/null +++ b/src/tests/wayland-test-clients/xdg-session-management-replace.c @@ -0,0 +1,239 @@ +/* + * 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 "wayland-test-client-utils.h" + +#include "session-management-v1-client-protocol.h" + + +typedef enum _TestState +{ + TEST_STATE_INIT = 0, + TEST_STATE_RECEIVE_REPLACED = 1, + TEST_STATE_ASSERT_RESTORED = 2, +} TestState; + +typedef struct _TestDisplayState +{ + struct xx_session_manager_v1 *session_manager; + TestState state; +} TestDisplayState; + +static void +handle_registry_global (void *user_data, + struct wl_registry *registry, + uint32_t id, + const char *interface, + uint32_t version) +{ + WaylandDisplay *display = user_data; + TestDisplayState *test_state = display->test_state; + + if (strcmp (interface, "xx_session_manager_v1") == 0) + { + test_state->session_manager = + wl_registry_bind (registry, id, &xx_session_manager_v1_interface, 1); + } +} + +static void +handle_registry_global_remove (void *user_data, + struct wl_registry *registry, + uint32_t name) +{ +} + +static const struct wl_registry_listener registry_listener = { + handle_registry_global, + handle_registry_global_remove +}; + +typedef struct +{ + gboolean received_created; + gboolean received_restored; + gboolean received_replaced; + char *id; +} TestCreateState; + +typedef struct +{ + gboolean configured; + gboolean restored; +} ToplevelSessionState; + +static void +test_create_created (void *user_data, + struct xx_session_v1 *xdg_session_v1, + const char *id) +{ + TestCreateState *state = user_data; + + state->received_created = TRUE; + state->id = g_strdup (id); +} + +static void +test_create_restored (void *user_data, + struct xx_session_v1 *xdg_session_v1) +{ + TestCreateState *state = user_data; + + state->received_restored = TRUE; +} + +static void +test_create_replaced (void *user_data, + struct xx_session_v1 *xdg_session_v1) +{ + TestCreateState *state = user_data; + + state->received_replaced = TRUE; +} + +static struct xx_session_v1_listener test_create_session_listener = { + test_create_created, + test_create_restored, + test_create_replaced, +}; + +static void +toplevel_restored (void *user_data, + struct xx_toplevel_session_v1 *toplevel_session, + struct xdg_toplevel *toplevel) +{ + ToplevelSessionState *toplevel_state = user_data; + + toplevel_state->restored = TRUE; +} + +static struct xx_toplevel_session_v1_listener toplevel_session_listener = { + toplevel_restored, +}; + +static void +on_toplevel_configured (WaylandSurface *surface, + ToplevelSessionState *toplevel_state) +{ + toplevel_state->configured = TRUE; +} + +int +main (int argc, + char **argv) +{ + g_autoptr (WaylandDisplay) display1 = NULL, display2 = NULL; + struct wl_registry *registry1, *registry2; + TestDisplayState *test_state1, *test_state2; + g_autoptr (WaylandSurface) toplevel1 = NULL, toplevel2 = NULL; + struct xx_session_v1 *session1, *session2; + struct xx_toplevel_session_v1 *toplevel_session1, *toplevel_session2; + TestCreateState state1 = {}; + TestCreateState state2 = {}; + ToplevelSessionState toplevel_state1 = {}; + ToplevelSessionState toplevel_state2 = {}; + + display1 = wayland_display_new (WAYLAND_DISPLAY_CAPABILITY_TEST_DRIVER); + test_state1 = g_new0 (TestDisplayState, 1); + display1->test_state = test_state1; + display1->destroy_test_state = g_free; + + registry1 = wl_display_get_registry (display1->display); + wl_registry_add_listener (registry1, ®istry_listener, display1); + wl_display_roundtrip (display1->display); + + g_assert_nonnull (test_state1->session_manager); + + toplevel1 = wayland_surface_new (display1, "toplevel", + 100, 100, 0xff50ff50); + g_signal_connect (toplevel1, "configure", + G_CALLBACK (on_toplevel_configured), + &toplevel_state1); + + session1 = + xx_session_manager_v1_get_session (test_state1->session_manager, + XX_SESSION_MANAGER_V1_REASON_LAUNCH, + NULL); + xx_session_v1_add_listener (session1, &test_create_session_listener, &state1); + + while (!state1.received_created) + wayland_display_dispatch (display1); + g_assert_nonnull (state1.id); + + /* Test add before committing initial state. */ + toplevel_session1 = xx_session_v1_add_toplevel (session1, + toplevel1->xdg_toplevel, + "toplevel"); + xx_toplevel_session_v1_add_listener (toplevel_session1, + &toplevel_session_listener, + &toplevel_state1); + wl_surface_commit (toplevel1->wl_surface); + + while (!toplevel_state1.configured) + wayland_display_dispatch (display1); + g_assert_false (toplevel_state1.restored); + + display2 = wayland_display_new (WAYLAND_DISPLAY_CAPABILITY_TEST_DRIVER); + test_state2 = g_new0 (TestDisplayState, 1); + display2->test_state = test_state2; + display2->destroy_test_state = g_free; + + registry2 = wl_display_get_registry (display2->display); + wl_registry_add_listener (registry2, ®istry_listener, display2); + wl_display_roundtrip (display2->display); + + g_assert_nonnull (test_state2->session_manager); + + toplevel2 = wayland_surface_new (display2, "toplevel", + 100, 100, 0xff50ff50); + g_signal_connect (toplevel2, "configure", + G_CALLBACK (on_toplevel_configured), + &toplevel_state2); + + session2 = + xx_session_manager_v1_get_session (test_state2->session_manager, + XX_SESSION_MANAGER_V1_REASON_LAUNCH, + state1.id); + xx_session_v1_add_listener (session2, &test_create_session_listener, &state2); + + while (!state2.received_restored) + wayland_display_dispatch (display2); + + /* Test add before committing initial state. */ + toplevel_session2 = xx_session_v1_restore_toplevel (session2, + toplevel2->xdg_toplevel, + "toplevel"); + xx_toplevel_session_v1_add_listener (toplevel_session2, + &toplevel_session_listener, + &toplevel_state2); + wl_surface_commit (toplevel2->wl_surface); + + while (!toplevel_state2.configured) + wayland_display_dispatch (display2); + + /* check that the first client received the replaced event */ + while (!state1.received_replaced) + wayland_display_dispatch (display1); + + /* TODO: check that client1 is now inert */ + + return EXIT_SUCCESS; +} diff --git a/src/tests/wayland-test-clients/xdg-session-management.c b/src/tests/wayland-test-clients/xdg-session-management.c new file mode 100644 index 000000000..9da519755 --- /dev/null +++ b/src/tests/wayland-test-clients/xdg-session-management.c @@ -0,0 +1,256 @@ +/* + * 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 "wayland-test-client-utils.h" + +#include "session-management-v1-client-protocol.h" + +typedef struct _TestDisplayState +{ + struct xx_session_manager_v1 *session_manager; +} TestDisplayState; + +static void +handle_registry_global (void *user_data, + struct wl_registry *registry, + uint32_t id, + const char *interface, + uint32_t version) +{ + WaylandDisplay *display = user_data; + TestDisplayState *test_state = display->test_state; + + if (strcmp (interface, "xx_session_manager_v1") == 0) + { + test_state->session_manager = + wl_registry_bind (registry, id, &xx_session_manager_v1_interface, 1); + } +} + +static void +handle_registry_global_remove (void *user_data, + struct wl_registry *registry, + uint32_t name) +{ +} + +static const struct wl_registry_listener registry_listener = { + handle_registry_global, + handle_registry_global_remove +}; + +typedef struct +{ + gboolean received_created; +} TestCreateState; + +typedef struct +{ + gboolean configured; + gboolean restored; +} ToplevelSessionState; + +static void +test_create_created (void *user_data, + struct xx_session_v1 *xdg_session_v1, + const char *id) +{ + TestCreateState *state = user_data; + + state->received_created = TRUE; +} + +static void +test_create_restored (void *user_data, + struct xx_session_v1 *xdg_session_v1) +{ +} + +static void +test_create_replaced (void *user_data, + struct xx_session_v1 *xdg_session_v1) +{ +} + +static struct xx_session_v1_listener test_create_session_listener = { + test_create_created, + test_create_restored, + test_create_replaced, +}; + +static void +toplevel_restored (void *user_data, + struct xx_toplevel_session_v1 *toplevel_session, + struct xdg_toplevel *toplevel) +{ + ToplevelSessionState *toplevel_state = user_data; + + toplevel_state->restored = TRUE; +} + +static struct xx_toplevel_session_v1_listener toplevel_session_listener = { + toplevel_restored, +}; + +static void +on_toplevel_configured (WaylandSurface *surface, + ToplevelSessionState *toplevel_state) +{ + toplevel_state->configured = TRUE; +} + +static void +basic (WaylandDisplay *display) +{ + TestDisplayState *test_state = display->test_state; + g_autoptr (WaylandSurface) toplevel1 = NULL; + g_autoptr (WaylandSurface) toplevel2 = NULL; + struct xx_session_v1 *session; + struct xx_toplevel_session_v1 *toplevel_session1; + struct xx_toplevel_session_v1 *toplevel_session2; + TestCreateState state = {}; + ToplevelSessionState toplevel_state1 = {}; + ToplevelSessionState toplevel_state2 = {}; + + toplevel1 = wayland_surface_new (display, "toplevel1", + 100, 100, 0xff50ff50); + g_signal_connect (toplevel1, "configure", + G_CALLBACK (on_toplevel_configured), + &toplevel_state1); + + session = + xx_session_manager_v1_get_session (test_state->session_manager, + XX_SESSION_MANAGER_V1_REASON_LAUNCH, + NULL); + xx_session_v1_add_listener (session, &test_create_session_listener, &state); + + while (!state.received_created) + wayland_display_dispatch (display); + + /* Test add before committing initial state. */ + toplevel_session1 = xx_session_v1_add_toplevel (session, + toplevel1->xdg_toplevel, + "toplevel1"); + xx_toplevel_session_v1_add_listener (toplevel_session1, + &toplevel_session_listener, + &toplevel_state1); + wl_surface_commit (toplevel1->wl_surface); + + while (!toplevel_state1.configured) + wayland_display_dispatch (display); + g_assert_false (toplevel_state1.restored); + + /* Test add after committing initial state. */ + toplevel2 = wayland_surface_new (display, "toplevel2", + 100, 100, 0xff0000ff); + g_signal_connect (toplevel1, "configure", + G_CALLBACK (on_toplevel_configured), + &toplevel_state2); + wl_surface_commit (toplevel1->wl_surface); + + toplevel_session2 = xx_session_v1_add_toplevel (session, + toplevel2->xdg_toplevel, + "toplevel2"); + xx_toplevel_session_v1_add_listener (toplevel_session2, + &toplevel_session_listener, + &toplevel_state2); + + while (!toplevel_state2.configured) + wayland_display_dispatch (display); + g_assert_false (toplevel_state2.restored); + + xx_toplevel_session_v1_destroy (toplevel_session1); + xx_toplevel_session_v1_destroy (toplevel_session2); + xx_session_v1_destroy (session); +} + +static void +toplevel_inert (WaylandDisplay *display) +{ + TestDisplayState *test_state = display->test_state; + g_autoptr (WaylandSurface) toplevel = NULL; + struct xx_session_v1 *session; + struct xx_toplevel_session_v1 *toplevel_session; + TestCreateState state = {}; + ToplevelSessionState toplevel_state = {}; + + toplevel = wayland_surface_new (display, "toplevel", + 100, 100, 0xff50ff50); + g_signal_connect (toplevel, "configure", + G_CALLBACK (on_toplevel_configured), + &toplevel_state); + + session = + xx_session_manager_v1_get_session (test_state->session_manager, + XX_SESSION_MANAGER_V1_REASON_LAUNCH, + NULL); + xx_session_v1_add_listener (session, &test_create_session_listener, &state); + + while (!state.received_created) + wayland_display_dispatch (display); + + /* Test add before committing initial state. */ + toplevel_session = xx_session_v1_add_toplevel (session, + toplevel->xdg_toplevel, + "toplevel"); + xx_toplevel_session_v1_add_listener (toplevel_session, + &toplevel_session_listener, + &toplevel_state); + wl_surface_commit (toplevel->wl_surface); + + while (!toplevel_state.configured) + wayland_display_dispatch (display); + g_assert_false (toplevel_state.restored); + + /* destroy the xdg_toplevel */ + g_clear_object (&toplevel); + + /* toplevel_session should be inert now and remove should have no effect */ + xx_toplevel_session_v1_remove (toplevel_session); + + xx_session_v1_destroy (session); +} + +int +main (int argc, + char **argv) +{ + g_autoptr (WaylandDisplay) display = NULL; + struct wl_registry *registry; + TestDisplayState *test_state; + + display = wayland_display_new (WAYLAND_DISPLAY_CAPABILITY_TEST_DRIVER); + test_state = g_new0 (TestDisplayState, 1); + display->test_state = test_state; + display->destroy_test_state = g_free; + + registry = wl_display_get_registry (display->display); + wl_registry_add_listener (registry, ®istry_listener, display); + wl_display_roundtrip (display->display); + + g_assert_nonnull (test_state->session_manager); + + basic (display); + + toplevel_inert (display); + + return EXIT_SUCCESS; +} diff --git a/src/tests/wayland-unit-tests.c b/src/tests/wayland-unit-tests.c index df87bbce2..9c6feee68 100644 --- a/src/tests/wayland-unit-tests.c +++ b/src/tests/wayland-unit-tests.c @@ -572,6 +572,26 @@ toplevel_reuse_surface (void) meta_wayland_test_client_finish (wayland_test_client); } +static void +toplevel_sessions (void) +{ + MetaWaylandTestClient *wayland_test_client; + + wayland_test_client = + meta_wayland_test_client_new (test_context, "xdg-session-management"); + meta_wayland_test_client_finish (wayland_test_client); +} + +static void +toplevel_sessions_replace (void) +{ + MetaWaylandTestClient *wayland_test_client; + + wayland_test_client = + meta_wayland_test_client_new (test_context, "xdg-session-management-replace"); + meta_wayland_test_client_finish (wayland_test_client); +} + static gboolean mark_later_as_done (gpointer user_data) { @@ -1040,6 +1060,10 @@ 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", + toplevel_sessions_replace); #ifdef MUTTER_PRIVILEGED_TEST (void)(toplevel_bounds_struts); (void)(toplevel_bounds_monitors); @@ -1066,6 +1090,8 @@ main (int argc, g_autoptr (MetaContext) context = NULL; MetaTestRunFlags test_run_flags; + g_setenv ("MUTTER_DEBUG_SESSION_MANAGEMENT_PROTOCOL", "1", TRUE); + #ifdef MUTTER_PRIVILEGED_TEST context = meta_create_test_context (META_CONTEXT_TEST_TYPE_VKMS, META_CONTEXT_TEST_FLAG_NO_X11);