diff --git a/src/tests/cursor-tests-screen-cast-client.c b/src/tests/cursor-tests-screen-cast-client.c new file mode 100644 index 000000000..d597e54ff --- /dev/null +++ b/src/tests/cursor-tests-screen-cast-client.c @@ -0,0 +1,166 @@ +/* + * 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 "tests/remote-desktop-utils.h" + +#include "tests/meta-ref-test-utils.h" + +static MetaReftestFlag +reftest_flags_from_string (const char *flags) +{ + if (strcmp (flags, "update-ref") == 0) + return META_REFTEST_FLAG_UPDATE_REF; + g_assert_cmpstr (flags, ==, ""); + return META_REFTEST_FLAG_NONE; +} + +static void +draw_cursor (cairo_t *cr, + struct spa_meta_cursor *spa_cursor) +{ + struct spa_meta_bitmap *spa_bitmap; + cairo_surface_t *cursor_image; + + spa_bitmap = SPA_MEMBER (spa_cursor, spa_cursor->bitmap_offset, + struct spa_meta_bitmap); + g_assert_nonnull (spa_bitmap); + + cursor_image = + cairo_image_surface_create_for_data (SPA_MEMBER (spa_bitmap, + spa_bitmap->offset, + uint8_t), + CAIRO_FORMAT_ARGB32, + spa_bitmap->size.width, + spa_bitmap->size.height, + spa_bitmap->stride); + cairo_surface_mark_dirty (cursor_image); + + cairo_set_source_surface (cr, cursor_image, + spa_cursor->position.x - spa_cursor->hotspot.x, + spa_cursor->position.y - spa_cursor->hotspot.y); + cairo_paint (cr); +} + +static cairo_surface_t * +screen_cast_adaptor_capture (gpointer adaptor_data) +{ + Stream *stream = adaptor_data; + struct spa_buffer *spa_buffer; + cairo_surface_t *source_image; + cairo_surface_t *surface; + struct spa_meta_cursor *spa_cursor; + cairo_t *cr; + + g_assert_nonnull (stream->buffer); + + spa_buffer = stream->buffer->buffer; + g_assert_nonnull (spa_buffer->datas[0].data); + + source_image = + cairo_image_surface_create_for_data (spa_buffer->datas[0].data, + CAIRO_FORMAT_ARGB32, + stream->spa_format.size.width, + stream->spa_format.size.height, + spa_buffer->datas[0].chunk->stride); + cairo_surface_mark_dirty (source_image); + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + stream->spa_format.size.width, + stream->spa_format.size.height); + cr = cairo_create (surface); + cairo_set_source_surface (cr, source_image, 0.0, 0.0); + cairo_paint (cr); + + spa_cursor = spa_buffer_find_meta_data (spa_buffer, SPA_META_Cursor, + sizeof (*spa_cursor)); + + switch (stream->cursor_mode) + { + case CURSOR_MODE_HIDDEN: + case CURSOR_MODE_EMBEDDED: + g_assert_false (spa_meta_cursor_is_valid (spa_cursor)); + break; + case CURSOR_MODE_METADATA: + g_assert_true (spa_meta_cursor_is_valid (spa_cursor)); + draw_cursor (cr, spa_cursor); + break; + } + + cairo_destroy (cr); + + return surface; +} + +int +main (int argc, + char **argv) +{ + ScreenCast *screen_cast; + Session *session; + Stream *stream; + const char *test_name; + int test_seq; + CursorMode cursor_mode; + MetaReftestFlag ref_test_flags; + + g_test_init (&argc, &argv, NULL); + + g_assert_cmpint (argc, ==, 5); + + test_name = argv[1]; + test_seq = atoi (argv[2]); + cursor_mode = cursor_mode_from_string (argv[3]); + ref_test_flags = reftest_flags_from_string (argv[4]); + + g_debug ("Verifying screen cast cursor mode %s for test case %s", + argv[2], test_name); + + g_debug ("Initializing PipeWire"); + init_pipewire (); + + g_debug ("Creating screen cast session"); + screen_cast = screen_cast_new (); + session = screen_cast_create_session (NULL, screen_cast); + stream = session_record_monitor (session, NULL, cursor_mode); + + g_debug ("Starting screen cast stream"); + session_start (session); + + stream_wait_for_render (stream); + + meta_ref_test_verify (screen_cast_adaptor_capture, + stream, + test_name, + test_seq, + ref_test_flags); + + g_debug ("Stopping session"); + + session_stop (session); + stream_free (stream); + session_free (session); + screen_cast_free (screen_cast); + + release_pipewire (); + + g_debug ("Done"); + + return EXIT_SUCCESS; +} diff --git a/src/tests/cursor-tests.c b/src/tests/cursor-tests.c index 47aa18af5..a90525adc 100644 --- a/src/tests/cursor-tests.c +++ b/src/tests/cursor-tests.c @@ -21,6 +21,7 @@ #include "backends/meta-cursor-sprite-xcursor.h" #include "backends/meta-cursor-tracker-private.h" #include "backends/meta-logical-monitor.h" +#include "backends/meta-screen-cast.h" #include "clutter/clutter.h" #include "compositor/meta-window-actor-private.h" #include "core/meta-fraction.h" @@ -254,6 +255,52 @@ layout_mode_to_string (MetaLogicalMonitorLayoutMode layout_mode) g_assert_not_reached (); } +static const char * +cursor_mode_to_string (MetaScreenCastCursorMode cursor_mode) +{ + switch (cursor_mode) + { + case META_SCREEN_CAST_CURSOR_MODE_HIDDEN: + return "hidden"; + case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED: + return "embedded"; + case META_SCREEN_CAST_CURSOR_MODE_METADATA: + return "metadata"; + } + + g_assert_not_reached (); +} + +static const char * +reftest_flags_to_string (MetaReftestFlag flags) +{ + if (flags & META_REFTEST_FLAG_UPDATE_REF) + return "update-ref"; + else + return ""; +} + +static void +verify_screen_cast_content (const char *ref_test_name, + int test_seq_no, + MetaScreenCastCursorMode cursor_mode) +{ + g_autoptr (GSubprocess) subprocess = NULL; + g_autofree char *test_seq_no_string = NULL; + MetaReftestFlag reftest_flags; + + test_seq_no_string = g_strdup_printf ("%d", test_seq_no); + reftest_flags = META_REFTEST_FLAG_NONE; + subprocess = + meta_launch_test_executable ("mutter-cursor-tests-screen-cast-client", + ref_test_name, + test_seq_no_string, + cursor_mode_to_string (cursor_mode), + reftest_flags_to_string (reftest_flags), + NULL); + meta_wait_test_process (subprocess); +} + static void wait_for_no_windows (void) { @@ -318,6 +365,13 @@ test_client_cursor (ClutterStageView *view, ref_test_seq, ref_test_flags); + verify_screen_cast_content (ref_test_name, + ref_test_seq, + META_SCREEN_CAST_CURSOR_MODE_EMBEDDED); + verify_screen_cast_content (ref_test_name, + ref_test_seq, + META_SCREEN_CAST_CURSOR_MODE_METADATA); + meta_wayland_test_driver_emit_sync_event (test_driver, 0); g_object_add_weak_pointer (G_OBJECT (window_actor), @@ -397,6 +451,10 @@ meta_test_native_cursor_scaling (void) ref_test_name, 0, meta_ref_test_determine_ref_test_flag ()); + verify_screen_cast_content (ref_test_name, 0, + META_SCREEN_CAST_CURSOR_MODE_EMBEDDED); + verify_screen_cast_content (ref_test_name, 0, + META_SCREEN_CAST_CURSOR_MODE_METADATA); test_client_cursor (view, CURSOR_SCALE_METHOD_BUFFER_SCALE, diff --git a/src/tests/meson.build b/src/tests/meson.build index ce3ffddec..9e449a2f5 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -390,6 +390,33 @@ input_capture_client = executable('mutter-input-capture-test-client', install_dir: mutter_installed_tests_libexecdir, ) +reftest_deps = [ + cairo_dep, + glib_dep, +] + +cursor_screen_cast_client = executable('mutter-cursor-tests-screen-cast-client', + sources: [ + 'cursor-tests-screen-cast-client.c', + 'meta-ref-test-utils.c', + 'meta-ref-test-utils.h', + remote_desktop_utils, + ], + include_directories: tests_includes, + c_args: [ + tests_c_args, + '-DG_LOG_DOMAIN="mutter-cursor-tests-screen-cast-client"', + ], + dependencies: [ + gio_dep, + libpipewire_dep, + reftest_deps, + ], + install: have_installed_tests, + install_dir: mutter_installed_tests_libexecdir, + install_rpath: pkglibdir, +) + # Native backend tests test_cases += [ { @@ -462,6 +489,7 @@ test_cases += [ ], 'depends': [ test_client_executables.get('cursor-tests-client'), + cursor_screen_cast_client, ] }, ] diff --git a/src/tests/remote-desktop-utils.c b/src/tests/remote-desktop-utils.c index 23a2f2594..f1ccb770f 100644 --- a/src/tests/remote-desktop-utils.c +++ b/src/tests/remote-desktop-utils.c @@ -430,6 +430,16 @@ stream_resize (Stream *stream, params, G_N_ELEMENTS (params)); } +void +stream_wait_for_render (Stream *stream) +{ + int initial_buffer_count = stream->buffer_count; + + g_debug ("Waiting for new buffer"); + while (stream->buffer_count == initial_buffer_count) + g_main_context_iteration (NULL, TRUE); +} + static void on_pipewire_stream_added (MetaDBusScreenCastStream *proxy, unsigned int node_id, diff --git a/src/tests/remote-desktop-utils.h b/src/tests/remote-desktop-utils.h index 4017be39d..d3053fba5 100644 --- a/src/tests/remote-desktop-utils.h +++ b/src/tests/remote-desktop-utils.h @@ -82,6 +82,19 @@ typedef struct _ScreenCast g_assert_cmpint (stream->cursor_y, ==, (y)); \ } +static inline CursorMode +cursor_mode_from_string (const char *name) +{ + if (strcmp (name, "hidden") == 0) + return CURSOR_MODE_HIDDEN; + else if (strcmp (name, "embedded") == 0) + return CURSOR_MODE_EMBEDDED; + else if (strcmp (name, "metadata") == 0) + return CURSOR_MODE_METADATA; + + g_assert_not_reached (); +} + void release_pipewire (void); void init_pipewire (void); @@ -90,6 +103,8 @@ void stream_resize (Stream *stream, int width, int height); +void stream_wait_for_render (Stream *stream); + void stream_free (Stream *stream); void session_notify_absolute_pointer (Session *session, diff --git a/src/tests/screen-cast-client.c b/src/tests/screen-cast-client.c index 560740c8f..6a475cf17 100644 --- a/src/tests/screen-cast-client.c +++ b/src/tests/screen-cast-client.c @@ -45,16 +45,6 @@ stream_wait_for_streaming (Stream *stream) g_main_context_iteration (NULL, TRUE); } -static void -stream_wait_for_render (Stream *stream) -{ - int initial_buffer_count = stream->buffer_count; - - g_debug ("Waiting for new buffer"); - while (stream->buffer_count == initial_buffer_count) - g_main_context_iteration (NULL, TRUE); -} - int main (int argc, char **argv)