From 0c9a31459a5bbb21538f42b0f1c4879fa27f951a Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Fri, 28 Feb 2025 00:35:34 +0100 Subject: [PATCH] tests/wayland-unit-tests: Add cursor shape tests Tests error conditions, selecting a cursor shape, switching back to a surface based cursor, and that the shape persists when the cursor shape device is destoryed. Part-of: --- src/tests/meson.build | 1 + .../ref-tests/wayland_cursor_shape_0.ref.png | Bin 0 -> 1989 bytes .../ref-tests/wayland_cursor_shape_1.ref.png | Bin 0 -> 2563 bytes .../ref-tests/wayland_cursor_shape_2.ref.png | Bin 0 -> 2782 bytes src/tests/wayland-test-clients/cursor-shape.c | 137 ++++++++++++++++++ src/tests/wayland-test-clients/meson.build | 3 + src/tests/wayland-unit-tests.c | 55 +++++++ 7 files changed, 196 insertions(+) create mode 100644 src/tests/ref-tests/wayland_cursor_shape_0.ref.png create mode 100644 src/tests/ref-tests/wayland_cursor_shape_1.ref.png create mode 100644 src/tests/ref-tests/wayland_cursor_shape_2.ref.png create mode 100644 src/tests/wayland-test-clients/cursor-shape.c 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 0000000000000000000000000000000000000000..df39d782b05d72cfebd73c5576ff29d3ea8e251c GIT binary patch literal 1989 zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sV0^&A1Qgk|*?TjPVoUONcVYMsf(!O8pUl9( zUghcH7*a9k?VXLhhYffb98aG3r)?}PQNU%}b7yPL4ci+9=g%-PoZIz^mEnOrrvU>C ziv$A`6AuF;BU?iQ1G9re1EYe%0R{nq1O^U{QR&fO8BGoYkqthq6lbX0etqA}bF-Rv zGo0T+I9EOp7TYr;KKw(R-e}GpEzm{_a(Y)_2gGGoa_u*prJe|^ml-@={an^LB{Ts5 Dr#&JG literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..8507c222c023abbcd38b7cd40b3e65986f737dfa GIT binary patch literal 2563 zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sV0^&A1Qgk|*?TjPVoUONcVYMsf(!O8pUl9( zxx>@NF{EP7+q=5?A@x!X59c**bMTUg>tYP@;dNZtxTK54WS;+l?FSToXjsTF3Qd@> zpkToQ#>NM#Q~Zo=wLJu8ulce5#_r2|^~&!gt9^fOnSSn!LEmE`h7bEqSF$rS{C8*K zVPIrrYiM9#c5rB5R8Tm;ARv&yz`>RVUbse`>7OKHTdPUmAfBNU2f4;xJf4ooDy6B0AmH3DEEE0UpYuBzVl!@O{@$tjM!@a$|>F4LIU9(0< zUw{4j_4#>u>(;CRT4d5(9lW9A+2^0n&(GJ7*_Se;Zes(rd;@RZYs_o&G zmBIhCB=~fs4Rlr|N__kF?cToH>St$Wo;`c^W=`4bYiohd2L`x%zueih%{Ozx&U5^6 z2N?^DoA~{8ckkVs=c6XB9~ToF`}fn+)9d5+&$F#A`~UCn%aUEdpx?aNc&?wi@best zjvAvxMc%_3wrwi|y885V{rNVPMWv;sR&)RT`1ttV-fC%S>B}!mWRBm=dDg3XqI@$uUk4uDJ44$rjF6*2UngIWP Bc~k%Z literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f8b69d0374338933b3e497d61cfaa0778221ad12 GIT binary patch literal 2782 zcmeHJYfRc#6u&UF)ZsMSs8I;(V@4@_olRV>blC?JuE zTaA*{8MPuBP38=~D6!VJjuoR+$od^Ju0{~(fg@kv8BykdW^2m&fi3ZN2(K^~u3xH>BZ1kr| z#RA=HIraS%BxmJ(F8cV5--DintX#PTL~DrqK8kxke7oUyN+C)CjOuLo%w*}Nzv43k_1q#reEOPNgDphMLh^w4a;>X{DPfnPe40%4NNPvS!Q<=?8O z_yK&4Wo^3FFm_0_7Giibf3wLXjAC49nk}N!XStM{1a~j_Hn(o09-BUwCFvb^SZC_W zY}--pmZto%Mu*^4o7rr}>09Nzpzj##j7NW^4HkSOCSPq43WaIoY*t>L&StZ% z{&a4jsi_IF8M*!0axAfKh{GP ze!m232-LhhmeX#|SC0{+qL{|z$=N3S!h&5!i4FhWU7izxg5z#;x!mgNYB*GLbMsw& zX;IM=vG||KD#AOkd3*Eu{D7$9``fQRj%lSd!nCf3p{20Ebc+K9^>|rPk!kf=6`T?F z{(OoQmz$e=wc-qqg5h;{cek*uu`EhO148ZZNVO-YXkxNJD2&HG|LXSbFVo54M?V;L6}W1(t5(0cTJ7qG{ZCYVGAb! literal 0 HcmV?d00001 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,