[mutter] screen-cast-window-stream: Add support for cursor modes



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]