tests/cursor-tests: Also test cursor rendering in screen casting

This uses the ref test infrastructure inside the cursor tests screen
cast client, and verifies the content on the screen cast matches the
content of the compositor, both when using the 'embedded' cursor mode,
and the 'metadata' cursor mode.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3859>
This commit is contained in:
Jonas Ådahl 2024-11-20 21:36:16 +01:00 committed by Sebastian Wick
parent bcb52960db
commit a9a4923c03
6 changed files with 277 additions and 10 deletions

View File

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#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;
}

View File

@ -21,6 +21,7 @@
#include "backends/meta-cursor-sprite-xcursor.h" #include "backends/meta-cursor-sprite-xcursor.h"
#include "backends/meta-cursor-tracker-private.h" #include "backends/meta-cursor-tracker-private.h"
#include "backends/meta-logical-monitor.h" #include "backends/meta-logical-monitor.h"
#include "backends/meta-screen-cast.h"
#include "clutter/clutter.h" #include "clutter/clutter.h"
#include "compositor/meta-window-actor-private.h" #include "compositor/meta-window-actor-private.h"
#include "core/meta-fraction.h" #include "core/meta-fraction.h"
@ -254,6 +255,52 @@ layout_mode_to_string (MetaLogicalMonitorLayoutMode layout_mode)
g_assert_not_reached (); 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 static void
wait_for_no_windows (void) wait_for_no_windows (void)
{ {
@ -318,6 +365,13 @@ test_client_cursor (ClutterStageView *view,
ref_test_seq, ref_test_seq,
ref_test_flags); 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); meta_wayland_test_driver_emit_sync_event (test_driver, 0);
g_object_add_weak_pointer (G_OBJECT (window_actor), g_object_add_weak_pointer (G_OBJECT (window_actor),
@ -397,6 +451,10 @@ meta_test_native_cursor_scaling (void)
ref_test_name, ref_test_name,
0, 0,
meta_ref_test_determine_ref_test_flag ()); 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, test_client_cursor (view,
CURSOR_SCALE_METHOD_BUFFER_SCALE, CURSOR_SCALE_METHOD_BUFFER_SCALE,

View File

@ -390,6 +390,33 @@ input_capture_client = executable('mutter-input-capture-test-client',
install_dir: mutter_installed_tests_libexecdir, 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 # Native backend tests
test_cases += [ test_cases += [
{ {
@ -462,6 +489,7 @@ test_cases += [
], ],
'depends': [ 'depends': [
test_client_executables.get('cursor-tests-client'), test_client_executables.get('cursor-tests-client'),
cursor_screen_cast_client,
] ]
}, },
] ]

View File

@ -430,6 +430,16 @@ stream_resize (Stream *stream,
params, G_N_ELEMENTS (params)); 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 static void
on_pipewire_stream_added (MetaDBusScreenCastStream *proxy, on_pipewire_stream_added (MetaDBusScreenCastStream *proxy,
unsigned int node_id, unsigned int node_id,

View File

@ -82,6 +82,19 @@ typedef struct _ScreenCast
g_assert_cmpint (stream->cursor_y, ==, (y)); \ 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 release_pipewire (void);
void init_pipewire (void); void init_pipewire (void);
@ -90,6 +103,8 @@ void stream_resize (Stream *stream,
int width, int width,
int height); int height);
void stream_wait_for_render (Stream *stream);
void stream_free (Stream *stream); void stream_free (Stream *stream);
void session_notify_absolute_pointer (Session *session, void session_notify_absolute_pointer (Session *session,

View File

@ -45,16 +45,6 @@ stream_wait_for_streaming (Stream *stream)
g_main_context_iteration (NULL, TRUE); 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 int
main (int argc, main (int argc,
char **argv) char **argv)