screen-cast-window-stream: Add support for cursor modes

Make the RecordWindow method also understand the 'cursor-mode' property.
For 'embedded' the cursor is drawn onto the pixel buffer using cairo,
otherwise it works similarly to how RecordMonitor deals with it.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/413
This commit is contained in:
Jonas Ådahl 2019-01-24 18:43:27 +01:00
parent 1760a6e560
commit 9a20271f90
5 changed files with 312 additions and 13 deletions

View File

@ -383,6 +383,7 @@ handle_record_window (MetaDBusScreenCastSession *skeleton,
GDBusInterfaceSkeleton *interface_skeleton;
GDBusConnection *connection;
MetaWindow *window;
MetaScreenCastCursorMode cursor_mode;
GError *error = NULL;
MetaDisplay *display;
GVariant *window_id_variant = NULL;
@ -424,12 +425,28 @@ handle_record_window (MetaDBusScreenCastSession *skeleton,
return TRUE;
}
if (!g_variant_lookup (properties_variant, "cursor-mode", "u", &cursor_mode))
{
cursor_mode = META_SCREEN_CAST_CURSOR_MODE_HIDDEN;
}
else
{
if (!is_valid_cursor_mode (cursor_mode))
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Unknown cursor mode");
return TRUE;
}
}
interface_skeleton = G_DBUS_INTERFACE_SKELETON (skeleton);
connection = g_dbus_interface_skeleton_get_connection (interface_skeleton);
window_stream = meta_screen_cast_window_stream_new (session,
connection,
window,
cursor_mode,
&error);
if (!window_stream)
{

View File

@ -23,6 +23,7 @@
#include "backends/meta-screen-cast-window-stream-src.h"
#include "backends/meta-backend-private.h"
#include "backends/meta-screen-cast-session.h"
#include "backends/meta-screen-cast-window.h"
#include "backends/meta-screen-cast-window-stream.h"
#include "compositor/meta-window-actor-private.h"
@ -33,14 +34,32 @@ struct _MetaScreenCastWindowStreamSrc
MetaScreenCastWindow *screen_cast_window;
unsigned long screen_cast_window_before_paint_handler_id;
unsigned long screen_cast_window_after_paint_handler_id;
unsigned long screen_cast_window_destroyed_handler_id;
unsigned long cursor_moved_handler_id;
unsigned long cursor_changed_handler_id;
gboolean actor_was_dirty;
gboolean cursor_bitmap_invalid;
};
G_DEFINE_TYPE (MetaScreenCastWindowStreamSrc,
meta_screen_cast_window_stream_src,
META_TYPE_SCREEN_CAST_STREAM_SRC)
static MetaBackend *
get_backend (MetaScreenCastWindowStreamSrc *window_src)
{
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src);
MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
MetaScreenCastSession *session = meta_screen_cast_stream_get_session (stream);
MetaScreenCast *screen_cast =
meta_screen_cast_session_get_screen_cast (session);
return meta_screen_cast_get_backend (screen_cast);
}
static MetaScreenCastWindowStream *
get_window_stream (MetaScreenCastWindowStreamSrc *window_src)
{
@ -83,11 +102,91 @@ get_stream_height (MetaScreenCastWindowStreamSrc *window_src)
return meta_screen_cast_window_stream_get_height (window_stream);
}
static void
maybe_draw_cursor_sprite (MetaScreenCastWindowStreamSrc *window_src,
uint8_t *data,
MetaRectangle *stream_rect)
{
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src);
MetaBackend *backend = get_backend (window_src);
MetaCursorRenderer *cursor_renderer =
meta_backend_get_cursor_renderer (backend);
MetaCursorSprite *cursor_sprite;
CoglTexture *cursor_texture;
MetaScreenCastWindow *screen_cast_window;
ClutterPoint cursor_position;
ClutterPoint relative_cursor_position;
cairo_surface_t *cursor_surface;
uint8_t *cursor_surface_data;
GError *error = NULL;
cairo_surface_t *stream_surface;
int width, height;
float scale;
int hotspot_x, hotspot_y;
cairo_t *cr;
cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer);
if (!cursor_sprite)
return;
cursor_texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite);
if (!cursor_texture)
return;
screen_cast_window = window_src->screen_cast_window;
cursor_position = meta_cursor_renderer_get_position (cursor_renderer);
if (!meta_screen_cast_window_transform_cursor_position (screen_cast_window,
cursor_sprite,
&cursor_position,
&scale,
&relative_cursor_position))
return;
meta_cursor_sprite_get_hotspot (cursor_sprite, &hotspot_x, &hotspot_y);
width = cogl_texture_get_width (cursor_texture) * scale;
height = cogl_texture_get_height (cursor_texture) * scale;
cursor_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
width, height);
cursor_surface_data = cairo_image_surface_get_data (cursor_surface);
if (!meta_screen_cast_stream_src_draw_cursor_into (src,
cursor_texture,
scale,
cursor_surface_data,
&error))
{
g_warning ("Failed to draw cursor: %s", error->message);
g_error_free (error);
cairo_surface_destroy (cursor_surface);
return;
}
stream_surface =
cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32,
stream_rect->width,
stream_rect->height,
stream_rect->width * 4);
cr = cairo_create (stream_surface);
cairo_surface_mark_dirty (cursor_surface);
cairo_surface_flush (cursor_surface);
cairo_set_source_surface (cr, cursor_surface,
relative_cursor_position.x - hotspot_x * scale,
relative_cursor_position.y - hotspot_y * scale);
cairo_paint (cr);
cairo_destroy (cr);
cairo_surface_destroy (stream_surface);
cairo_surface_destroy (cursor_surface);
}
static gboolean
capture_into (MetaScreenCastWindowStreamSrc *window_src,
uint8_t *data)
{
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src);
MetaRectangle stream_rect;
MetaScreenCastStream *stream;
stream_rect.x = 0;
stream_rect.y = 0;
@ -97,6 +196,17 @@ capture_into (MetaScreenCastWindowStreamSrc *window_src,
meta_screen_cast_window_capture_into (window_src->screen_cast_window,
&stream_rect, data);
stream = meta_screen_cast_stream_src_get_stream (src);
switch (meta_screen_cast_stream_get_cursor_mode (stream))
{
case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
maybe_draw_cursor_sprite (window_src, data, &stream_rect);
break;
case META_SCREEN_CAST_CURSOR_MODE_METADATA:
case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
break;
}
return TRUE;
}
@ -139,9 +249,16 @@ static void
meta_screen_cast_window_stream_src_stop (MetaScreenCastWindowStreamSrc *window_src)
{
MetaBackend *backend = get_backend (window_src);
MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
if (!window_src->screen_cast_window)
return;
if (window_src->screen_cast_window_before_paint_handler_id)
g_signal_handler_disconnect (window_src->screen_cast_window,
window_src->screen_cast_window_before_paint_handler_id);
window_src->screen_cast_window_before_paint_handler_id = 0;
if (window_src->screen_cast_window_after_paint_handler_id)
g_signal_handler_disconnect (window_src->screen_cast_window,
@ -152,23 +269,97 @@ meta_screen_cast_window_stream_src_stop (MetaScreenCastWindowStreamSrc *window_s
g_signal_handler_disconnect (window_src->screen_cast_window,
window_src->screen_cast_window_destroyed_handler_id);
window_src->screen_cast_window_destroyed_handler_id = 0;
if (window_src->cursor_moved_handler_id)
g_signal_handler_disconnect (cursor_tracker,
window_src->cursor_moved_handler_id);
window_src->cursor_moved_handler_id = 0;
if (window_src->cursor_changed_handler_id)
g_signal_handler_disconnect (cursor_tracker,
window_src->cursor_changed_handler_id);
window_src->cursor_changed_handler_id = 0;
}
static void
screen_cast_window_after_paint (MetaScreenCastWindow *screen_cast_window,
screen_cast_window_before_paint (MetaScreenCastWindow *screen_cast_window,
MetaScreenCastWindowStreamSrc *window_src)
{
window_src->actor_was_dirty =
meta_screen_cast_window_has_damage (screen_cast_window);
}
static void
screen_cast_window_after_paint (MetaWindowActor *actor,
MetaScreenCastWindowStreamSrc *window_src)
{
if (window_src->actor_was_dirty)
{
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src);
meta_screen_cast_stream_src_maybe_record_frame (src);
}
}
static void
screen_cast_window_destroyed (MetaWindowActor *actor,
MetaScreenCastWindowStreamSrc *window_src)
{
meta_screen_cast_window_stream_src_stop (window_src);
window_src->screen_cast_window = NULL;
}
static gboolean
is_cursor_in_stream (MetaScreenCastWindowStreamSrc *window_src)
{
MetaBackend *backend = get_backend (window_src);
MetaCursorRenderer *cursor_renderer =
meta_backend_get_cursor_renderer (backend);
MetaCursorSprite *cursor_sprite;
ClutterPoint cursor_position;
MetaScreenCastWindow *screen_cast_window;
cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer);
cursor_position = meta_cursor_renderer_get_position (cursor_renderer);
screen_cast_window = window_src->screen_cast_window;
return meta_screen_cast_window_transform_cursor_position (screen_cast_window,
cursor_sprite,
&cursor_position,
NULL,
NULL);
}
static void
sync_cursor_state (MetaScreenCastWindowStreamSrc *window_src)
{
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src);
if (!is_cursor_in_stream (window_src))
return;
if (meta_screen_cast_window_has_damage (window_src->screen_cast_window))
return;
meta_screen_cast_stream_src_maybe_record_frame (src);
}
static void
screen_cast_window_destroyed (MetaScreenCastWindow *screen_cast_window,
MetaScreenCastWindowStreamSrc *window_src)
cursor_moved (MetaCursorTracker *cursor_tracker,
float x,
float y,
MetaScreenCastWindowStreamSrc *window_src)
{
meta_screen_cast_window_stream_src_stop (window_src);
window_src->screen_cast_window = NULL;
sync_cursor_state (window_src);
}
static void
cursor_changed (MetaCursorTracker *cursor_tracker,
MetaScreenCastWindowStreamSrc *window_src)
{
window_src->cursor_bitmap_invalid = TRUE;
sync_cursor_state (window_src);
}
static void
@ -176,7 +367,10 @@ meta_screen_cast_window_stream_src_enable (MetaScreenCastStreamSrc *src)
{
MetaScreenCastWindowStreamSrc *window_src =
META_SCREEN_CAST_WINDOW_STREAM_SRC (src);
MetaBackend *backend = get_backend (window_src);
MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
MetaWindowActor *window_actor;
MetaScreenCastStream *stream;
window_actor = meta_window_actor_from_window (get_window (window_src));
if (!window_actor)
@ -184,6 +378,11 @@ meta_screen_cast_window_stream_src_enable (MetaScreenCastStreamSrc *src)
window_src->screen_cast_window = META_SCREEN_CAST_WINDOW (window_actor);
window_src->screen_cast_window_before_paint_handler_id =
g_signal_connect (window_src->screen_cast_window,
"paint",
G_CALLBACK (screen_cast_window_before_paint),
window_src);
window_src->screen_cast_window_after_paint_handler_id =
g_signal_connect_after (window_src->screen_cast_window,
"paint",
@ -195,6 +394,24 @@ meta_screen_cast_window_stream_src_enable (MetaScreenCastStreamSrc *src)
"destroy",
G_CALLBACK (screen_cast_window_destroyed),
window_src);
stream = meta_screen_cast_stream_src_get_stream (src);
switch (meta_screen_cast_stream_get_cursor_mode (stream))
{
case META_SCREEN_CAST_CURSOR_MODE_METADATA:
case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
window_src->cursor_moved_handler_id =
g_signal_connect_after (cursor_tracker, "cursor-moved",
G_CALLBACK (cursor_moved),
window_src);
window_src->cursor_changed_handler_id =
g_signal_connect_after (cursor_tracker, "cursor-changed",
G_CALLBACK (cursor_changed),
window_src);
break;
case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
break;
}
}
static void
@ -218,6 +435,65 @@ meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src,
return TRUE;
}
static void
meta_screen_cast_window_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src,
struct spa_meta_cursor *spa_meta_cursor)
{
MetaScreenCastWindowStreamSrc *window_src =
META_SCREEN_CAST_WINDOW_STREAM_SRC (src);
MetaBackend *backend = get_backend (window_src);
MetaCursorRenderer *cursor_renderer =
meta_backend_get_cursor_renderer (backend);
MetaScreenCastWindow *screen_cast_window = window_src->screen_cast_window;
MetaCursorSprite *cursor_sprite;
ClutterPoint cursor_position;
float scale;
ClutterPoint relative_cursor_position;
int x, y;
cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer);
cursor_position = meta_cursor_renderer_get_position (cursor_renderer);
if (!meta_screen_cast_window_transform_cursor_position (screen_cast_window,
cursor_sprite,
&cursor_position,
&scale,
&relative_cursor_position))
{
meta_screen_cast_stream_src_unset_cursor_metadata (src,
spa_meta_cursor);
return;
}
x = (int) roundf (relative_cursor_position.x);
y = (int) roundf (relative_cursor_position.y);
if (window_src->cursor_bitmap_invalid)
{
if (cursor_sprite)
{
meta_screen_cast_stream_src_set_cursor_sprite_metadata (src,
spa_meta_cursor,
cursor_sprite,
x, y,
scale);
}
else
{
meta_screen_cast_stream_src_set_empty_cursor_sprite_metadata (src,
spa_meta_cursor,
x, y);
}
window_src->cursor_bitmap_invalid = FALSE;
}
else
{
meta_screen_cast_stream_src_set_cursor_position_metadata (src,
spa_meta_cursor,
x, y);
}
}
MetaScreenCastWindowStreamSrc *
meta_screen_cast_window_stream_src_new (MetaScreenCastWindowStream *window_stream,
GError **error)
@ -230,6 +506,7 @@ meta_screen_cast_window_stream_src_new (MetaScreenCastWindowStream *window_stre
static void
meta_screen_cast_window_stream_src_init (MetaScreenCastWindowStreamSrc *window_src)
{
window_src->cursor_bitmap_invalid = TRUE;
}
static void
@ -243,4 +520,5 @@ meta_screen_cast_window_stream_src_class_init (MetaScreenCastWindowStreamSrcClas
src_class->disable = meta_screen_cast_window_stream_src_disable;
src_class->record_frame = meta_screen_cast_window_stream_src_record_frame;
src_class->get_videocrop = meta_screen_cast_window_stream_src_get_videocrop;
src_class->set_cursor_metadata = meta_screen_cast_window_stream_src_set_cursor_metadata;
}

View File

@ -80,16 +80,18 @@ meta_screen_cast_window_stream_get_height (MetaScreenCastWindowStream *window_st
}
MetaScreenCastWindowStream *
meta_screen_cast_window_stream_new (MetaScreenCastSession *session,
GDBusConnection *connection,
MetaWindow *window,
GError **error)
meta_screen_cast_window_stream_new (MetaScreenCastSession *session,
GDBusConnection *connection,
MetaWindow *window,
MetaScreenCastCursorMode cursor_mode,
GError **error)
{
return g_initable_new (META_TYPE_SCREEN_CAST_WINDOW_STREAM,
NULL,
error,
"session", session,
"connection", connection,
"cursor-mode", cursor_mode,
"window", window,
NULL);
}

View File

@ -32,10 +32,11 @@ G_DECLARE_FINAL_TYPE (MetaScreenCastWindowStream,
META, SCREEN_CAST_WINDOW_STREAM,
MetaScreenCastStream)
MetaScreenCastWindowStream * meta_screen_cast_window_stream_new (MetaScreenCastSession *session,
GDBusConnection *connection,
MetaWindow *window,
GError **error);
MetaScreenCastWindowStream * meta_screen_cast_window_stream_new (MetaScreenCastSession *session,
GDBusConnection *connection,
MetaWindow *window,
MetaScreenCastCursorMode cursor_mode,
GError **error);
MetaWindow * meta_screen_cast_window_stream_get_window (MetaScreenCastWindowStream *window_stream);
int meta_screen_cast_window_stream_get_width (MetaScreenCastWindowStream *window_stream);

View File

@ -97,6 +97,7 @@
Available @properties include:
* "window-id" (t): Id of the window to record.
* "cursor-mode" (u): Cursor mode. Default: 'hidden' (see RecordMonitor)
-->
<method name="RecordWindow">