diff --git a/src/tests/meson.build b/src/tests/meson.build index 078993d36..0bc6370e2 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -757,6 +757,7 @@ wayland_test_cases = [ 'depends': [ test_client, test_client_executables.get('buffer-transform'), + test_client_executables.get('cursor-shape'), test_client_executables.get('dma-buf-scanout'), test_client_executables.get('fractional-scale'), test_client_executables.get('fullscreen'), diff --git a/src/tests/ref-tests/wayland_cursor_shape_0.ref.png b/src/tests/ref-tests/wayland_cursor_shape_0.ref.png new file mode 100644 index 000000000..df39d782b Binary files /dev/null and b/src/tests/ref-tests/wayland_cursor_shape_0.ref.png differ diff --git a/src/tests/ref-tests/wayland_cursor_shape_1.ref.png b/src/tests/ref-tests/wayland_cursor_shape_1.ref.png new file mode 100644 index 000000000..8507c222c Binary files /dev/null and b/src/tests/ref-tests/wayland_cursor_shape_1.ref.png differ diff --git a/src/tests/ref-tests/wayland_cursor_shape_2.ref.png b/src/tests/ref-tests/wayland_cursor_shape_2.ref.png new file mode 100644 index 000000000..f8b69d037 Binary files /dev/null and b/src/tests/ref-tests/wayland_cursor_shape_2.ref.png differ diff --git a/src/tests/wayland-test-clients/cursor-shape.c b/src/tests/wayland-test-clients/cursor-shape.c new file mode 100644 index 000000000..c17d91e5e --- /dev/null +++ b/src/tests/wayland-test-clients/cursor-shape.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2025 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 "wayland-test-client-utils.h" + +static struct wl_pointer *pointer; +static uint32_t serial; + +static gboolean waiting_for_pointer_enter = FALSE; + +static void +on_pointer_enter (WaylandSurface *surface, + struct wl_pointer *pointer_l, + uint32_t serial_l) +{ + waiting_for_pointer_enter = FALSE; + pointer = pointer_l; + serial = serial_l; +} + +static void +wait_for_pointer_enter (WaylandSurface *surface) +{ + gulong handler_id; + + handler_id = g_signal_connect (surface, "pointer-enter", + G_CALLBACK (on_pointer_enter), NULL); + + waiting_for_pointer_enter = TRUE; + while (waiting_for_pointer_enter) + wayland_display_dispatch (surface->display); + + g_clear_signal_handler (&handler_id, surface); +} + +int +main (int argc, + char **argv) +{ + g_autoptr (WaylandDisplay) display = NULL; + g_autoptr (WaylandSurface) surface = NULL; + struct wl_surface *cursor_surface; + struct wp_cursor_shape_device_v1 *cursor_shape_device; + WaylandDisplayCapabilities caps = WAYLAND_DISPLAY_CAPABILITY_TEST_DRIVER; + + if (g_strcmp0 (argv[1], "v2-shape-on-v1") != 0) + caps |= WAYLAND_DISPLAY_CAPABILITY_CURSOR_SHAPE_V2; + + display = wayland_display_new (caps); + surface = wayland_surface_new (display, + "cursor-shape", + 100, 100, 0xffffffff); + xdg_toplevel_set_fullscreen (surface->xdg_toplevel, NULL); + wl_surface_commit (surface->wl_surface); + + wait_for_pointer_enter (surface); + wait_for_effects_completed (display, surface->wl_surface); + + cursor_surface = wl_compositor_create_surface (display->compositor); + draw_surface (display, cursor_surface, 10, 10, 0xff00ff00); + wl_surface_damage_buffer (cursor_surface, 0, 0, 10, 10); + wl_surface_commit (cursor_surface); + wl_pointer_set_cursor (pointer, serial, cursor_surface, 0, 0); + + g_assert (display->cursor_shape_mgr); + cursor_shape_device = + wp_cursor_shape_manager_v1_get_pointer (display->cursor_shape_mgr, pointer); + + if (g_strcmp0 (argv[1], "v2-shape-on-v1") == 0) + { + wp_cursor_shape_device_v1_set_shape (cursor_shape_device, + serial, + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_RESIZE); + + if (wl_display_dispatch (display->display) != -1) + return 1; + + return EXIT_SUCCESS; + } + else if (g_strcmp0 (argv[1], "bad-shape") == 0) + { + wp_cursor_shape_device_v1_set_shape (cursor_shape_device, + serial, + 3333); + + if (wl_display_dispatch (display->display) != -1) + return 1; + + return EXIT_SUCCESS; + } + else if (g_strcmp0 (argv[1], "ref-test") == 0) + { + /* make sure the surface cursor is still visible */ + wait_for_view_verified (display, 0); + /* make sure the default shape is visible */ + wp_cursor_shape_device_v1_set_shape (cursor_shape_device, + serial, + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT); + wait_for_view_verified (display, 1); + /* make sure switching back to the surface cursor works */ + wl_pointer_set_cursor (pointer, serial, cursor_surface, 0, 0); + wait_for_view_verified (display, 0); + /* make sure another shape works */ + wp_cursor_shape_device_v1_set_shape (cursor_shape_device, + serial, + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE); + wait_for_view_verified (display, 2); + /* destroy the wp_cursor_shape_device and make sure the shape persists */ + wp_cursor_shape_device_v1_set_shape (cursor_shape_device, + serial, + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT); + wp_cursor_shape_device_v1_destroy (cursor_shape_device); + wait_for_view_verified (display, 1); + + return EXIT_SUCCESS; + } + + + return 1; +} diff --git a/src/tests/wayland-test-clients/meson.build b/src/tests/wayland-test-clients/meson.build index d702a9497..9c2c5a613 100644 --- a/src/tests/wayland-test-clients/meson.build +++ b/src/tests/wayland-test-clients/meson.build @@ -21,6 +21,9 @@ wayland_test_clients = [ { 'name': 'color-management', }, + { + 'name': 'cursor-shape', + }, { 'name': 'dma-buf-scanout', }, diff --git a/src/tests/wayland-unit-tests.c b/src/tests/wayland-unit-tests.c index 00a3ec99b..84aa45dbd 100644 --- a/src/tests/wayland-unit-tests.c +++ b/src/tests/wayland-unit-tests.c @@ -53,6 +53,58 @@ find_client_window (const char *title) return meta_find_client_window (test_context, title); } +static void +cursor_shape (void) +{ + MetaBackend *backend = meta_context_get_backend (test_context); + ClutterSeat *seat = meta_backend_get_default_seat (backend); + MetaWaylandTestClient *wayland_test_client; + + virtual_pointer = clutter_seat_create_virtual_device (seat, + CLUTTER_POINTER_DEVICE); + + clutter_virtual_input_device_notify_absolute_motion (virtual_pointer, + g_get_monotonic_time (), + 320.0f, + 240.0f); + meta_flush_input (test_context); + + wayland_test_client = + meta_wayland_test_client_new_with_args (test_context, + "cursor-shape", + "v2-shape-on-v1", + NULL); + /* we wait for the window to flush out all the messages */ + meta_wait_for_client_window (test_context, "cursor-shape"); + g_test_expect_message ("libmutter", G_LOG_LEVEL_WARNING, + "WL: error in client communication*"); + meta_wayland_test_client_finish (wayland_test_client); + g_test_assert_expected_messages (); + + wayland_test_client = + meta_wayland_test_client_new_with_args (test_context, + "cursor-shape", + "bad-shape", + NULL); + /* we wait for the window to flush out all the messages */ + meta_wait_for_client_window (test_context, "cursor-shape"); + g_test_expect_message ("libmutter", G_LOG_LEVEL_WARNING, + "WL: error in client communication*"); + meta_wayland_test_client_finish (wayland_test_client); + g_test_assert_expected_messages (); + + /* FIXME workaround for a bug in native cursor renderer where just trying to + * get the cursor on a plane results in no software cursor being rendered */ + meta_backend_inhibit_hw_cursor (backend); + wayland_test_client = + meta_wayland_test_client_new_with_args (test_context, + "cursor-shape", + "ref-test", + NULL); + meta_wayland_test_client_finish (wayland_test_client); + meta_backend_uninhibit_hw_cursor (backend); +} + static void subsurface_remap_toplevel (void) { @@ -1327,6 +1379,8 @@ init_tests (void) toplevel_show_states); g_test_add_func ("/wayland/toplevel/suspended", toplevel_suspended); + g_test_add_func ("/wayland/cursor/shape", + cursor_shape); } int @@ -1337,6 +1391,7 @@ main (int argc, MetaTestRunFlags test_run_flags; g_setenv ("MUTTER_DEBUG_SESSION_MANAGEMENT_PROTOCOL", "1", TRUE); + g_setenv ("MUTTER_DEBUG_CURSOR_SHAPE_PROTOCOL", "1", TRUE); #ifdef MUTTER_PRIVILEGED_TEST context = meta_create_test_context (META_CONTEXT_TEST_TYPE_VKMS,