[mutter] screen-cast-window-stream: Add support for cursor modes
- From: Jonas Ådahl <jadahl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter] screen-cast-window-stream: Add support for cursor modes
- Date: Tue, 26 Feb 2019 13:52:43 +0000 (UTC)
commit 9a20271f900cfde225f5a2a98f9e93604ab89208
Author: Jonas Ådahl <jadahl gmail com>
Date: Thu Jan 24 18:43:27 2019 +0100
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
src/backends/meta-screen-cast-session.c | 17 ++
src/backends/meta-screen-cast-window-stream-src.c | 286 +++++++++++++++++++++-
src/backends/meta-screen-cast-window-stream.c | 10 +-
src/backends/meta-screen-cast-window-stream.h | 9 +-
src/org.gnome.Mutter.ScreenCast.xml | 1 +
5 files changed, 311 insertions(+), 12 deletions(-)
---
diff --git a/src/backends/meta-screen-cast-session.c b/src/backends/meta-screen-cast-session.c
index 130bd17c9..a9b861e8d 100644
--- a/src/backends/meta-screen-cast-session.c
+++ b/src/backends/meta-screen-cast-session.c
@@ -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)
{
diff --git a/src/backends/meta-screen-cast-window-stream-src.c
b/src/backends/meta-screen-cast-window-stream-src.c
index 0448b443f..dbf330420 100644
--- a/src/backends/meta-screen-cast-window-stream-src.c
+++ b/src/backends/meta-screen-cast-window-stream-src.c
@@ -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,31 +269,108 @@ 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)
{
- MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (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);
+ meta_screen_cast_stream_src_maybe_record_frame (src);
+ }
}
static void
-screen_cast_window_destroyed (MetaScreenCastWindow *screen_cast_window,
+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
+cursor_moved (MetaCursorTracker *cursor_tracker,
+ float x,
+ float y,
+ MetaScreenCastWindowStreamSrc *window_src)
+{
+ 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
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;
}
diff --git a/src/backends/meta-screen-cast-window-stream.c b/src/backends/meta-screen-cast-window-stream.c
index 19645540c..50d1806cd 100644
--- a/src/backends/meta-screen-cast-window-stream.c
+++ b/src/backends/meta-screen-cast-window-stream.c
@@ -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);
}
diff --git a/src/backends/meta-screen-cast-window-stream.h b/src/backends/meta-screen-cast-window-stream.h
index 3799be98a..6eae74f1c 100644
--- a/src/backends/meta-screen-cast-window-stream.h
+++ b/src/backends/meta-screen-cast-window-stream.h
@@ -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);
diff --git a/src/org.gnome.Mutter.ScreenCast.xml b/src/org.gnome.Mutter.ScreenCast.xml
index 953809727..8ec2a1919 100644
--- a/src/org.gnome.Mutter.ScreenCast.xml
+++ b/src/org.gnome.Mutter.ScreenCast.xml
@@ -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">
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]