mirror of
https://github.com/brl/mutter.git
synced 2025-01-10 19:52:25 +00:00
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:
parent
1760a6e560
commit
9a20271f90
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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">
|
||||
|
Loading…
Reference in New Issue
Block a user