[mutter] screen-cast: Add 'cursor-mode' to allow decoupled cursor updates



commit 4e402b397267169e578005cd89bb5bbdaf70853b
Author: Jonas Ådahl <jadahl gmail com>
Date:   Fri Dec 14 17:47:57 2018 +0100

    screen-cast: Add 'cursor-mode' to allow decoupled cursor updates
    
    The 'cursor-mode', which currently is limited to RecordMonitor(), allows
    the user to either do screen casts where the cursor is hidden, embedded
    in the framebuffer, or sent as PipeWire stream metadata.
    
    The latter allows the user to get cursor updates sent, including the
    cursor sprite, without requiring a stage paint each frame. Currently
    this is done by using the cursor sprite texture, and either reading
    directly from, or drawing to an offscreen framebuffer which is read from
    instead, in case the texture is scaled.
    
    https://gitlab.gnome.org/GNOME/mutter/merge_requests/357

 .gitlab-ci/Dockerfile                              |   3 +
 meson.build                                        |   2 +-
 src/backends/meta-screen-cast-monitor-stream-src.c | 416 ++++++++++++++++++++-
 src/backends/meta-screen-cast-monitor-stream.c     |  12 +-
 src/backends/meta-screen-cast-monitor-stream.h     |  11 +-
 src/backends/meta-screen-cast-session.c            |  31 ++
 src/backends/meta-screen-cast-stream-src.c         | 128 +++++--
 src/backends/meta-screen-cast-stream-src.h         |  24 +-
 src/backends/meta-screen-cast-stream.c             |  30 ++
 src/backends/meta-screen-cast-stream.h             |   2 +
 src/backends/meta-screen-cast-window-stream-src.c  |   4 +-
 src/backends/meta-screen-cast.h                    |   7 +
 src/org.gnome.Mutter.ScreenCast.xml                |  12 +-
 13 files changed, 625 insertions(+), 57 deletions(-)
---
diff --git a/.gitlab-ci/Dockerfile b/.gitlab-ci/Dockerfile
index 29db895f9..43cafa1ae 100644
--- a/.gitlab-ci/Dockerfile
+++ b/.gitlab-ci/Dockerfile
@@ -13,5 +13,8 @@ RUN dnf -y update && dnf -y upgrade && \
     # Unpackaged versions
     dnf install -y 
https://copr-be.cloud.fedoraproject.org/results/jadahl/mutter-ci/fedora-29-x86_64/00836095-gsettings-desktop-schemas/gsettings-desktop-schemas-3.30.1-1.20181206git918efdd69be53.fc29.x86_64.rpm
 
https://copr-be.cloud.fedoraproject.org/results/jadahl/mutter-ci/fedora-29-x86_64/00836095-gsettings-desktop-schemas/gsettings-desktop-schemas-devel-3.30.1-1.20181206git918efdd69be53.fc29.x86_64.rpm
 && \
 
+    # Packages not yet in stable
+    dnf install -y 
https://kojipkgs.fedoraproject.org//packages/pipewire/0.2.5/1.fc29/x86_64/pipewire-0.2.5-1.fc29.x86_64.rpm 
https://kojipkgs.fedoraproject.org//packages/pipewire/0.2.5/1.fc29/x86_64/pipewire-devel-0.2.5-1.fc29.x86_64.rpm
 
https://kojipkgs.fedoraproject.org//packages/pipewire/0.2.5/1.fc29/x86_64/pipewire-libs-0.2.5-1.fc29.x86_64.rpm
 && \
+
     dnf install -y intltool redhat-rpm-config make && \
     dnf clean all
diff --git a/meson.build b/meson.build
index 8d8f4da7b..be1b829a3 100644
--- a/meson.build
+++ b/meson.build
@@ -43,7 +43,7 @@ libinput_req = '>= 1.4'
 gbm_req = '>= 10.3'
 
 # screen cast version requirements
-libpipewire_req = '>= 0.2.2'
+libpipewire_req = '>= 0.2.5'
 
 gnome = import('gnome')
 pkg = import('pkgconfig')
diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c 
b/src/backends/meta-screen-cast-monitor-stream-src.c
index 2aad3aa0c..50ed6c519 100644
--- a/src/backends/meta-screen-cast-monitor-stream-src.c
+++ b/src/backends/meta-screen-cast-monitor-stream-src.c
@@ -24,23 +24,36 @@
 
 #include "backends/meta-screen-cast-monitor-stream-src.h"
 
+#include <spa/buffer/meta.h>
+
 #include "backends/meta-backend-private.h"
+#include "backends/meta-cursor-tracker-private.h"
 #include "backends/meta-logical-monitor.h"
 #include "backends/meta-monitor.h"
 #include "backends/meta-screen-cast-monitor-stream.h"
+#include "backends/meta-screen-cast-session.h"
 #include "clutter/clutter.h"
 #include "clutter/clutter-mutter.h"
+#include "core/boxes-private.h"
 
 struct _MetaScreenCastMonitorStreamSrc
 {
   MetaScreenCastStreamSrc parent;
 
   gulong actors_painted_handler_id;
+  gulong paint_handler_id;
+  gulong cursor_moved_handler_id;
+  gulong cursor_changed_handler_id;
 };
 
-G_DEFINE_TYPE (MetaScreenCastMonitorStreamSrc,
-               meta_screen_cast_monitor_stream_src,
-               META_TYPE_SCREEN_CAST_STREAM_SRC)
+static void
+hw_cursor_inhibitor_iface_init (MetaHwCursorInhibitorInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (MetaScreenCastMonitorStreamSrc,
+                         meta_screen_cast_monitor_stream_src,
+                         META_TYPE_SCREEN_CAST_STREAM_SRC,
+                         G_IMPLEMENT_INTERFACE (META_TYPE_HW_CURSOR_INHIBITOR,
+                                                hw_cursor_inhibitor_iface_init))
 
 static ClutterStage *
 get_stage (MetaScreenCastMonitorStreamSrc *monitor_src)
@@ -102,18 +115,163 @@ stage_painted (ClutterActor                   *actor,
   meta_screen_cast_stream_src_maybe_record_frame (src);
 }
 
+static MetaBackend *
+get_backend (MetaScreenCastMonitorStreamSrc *monitor_src)
+{
+  MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_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 gboolean
+is_cursor_in_stream (MetaScreenCastMonitorStreamSrc *monitor_src)
+{
+  MetaBackend *backend = get_backend (monitor_src);
+  MetaCursorRenderer *cursor_renderer =
+    meta_backend_get_cursor_renderer (backend);
+  MetaMonitor *monitor;
+  MetaLogicalMonitor *logical_monitor;
+  MetaRectangle logical_monitor_layout;
+  ClutterRect logical_monitor_rect;
+  MetaCursorSprite *cursor_sprite;
+
+  monitor = get_monitor (monitor_src);
+  logical_monitor = meta_monitor_get_logical_monitor (monitor);
+  logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor);
+  logical_monitor_rect =
+    meta_rectangle_to_clutter_rect (&logical_monitor_layout);
+
+  cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer);
+  if (cursor_sprite)
+    {
+      ClutterRect cursor_rect;
+
+      cursor_rect = meta_cursor_renderer_calculate_rect (cursor_renderer,
+                                                         cursor_sprite);
+      return clutter_rect_intersection (&cursor_rect,
+                                        &logical_monitor_rect,
+                                        NULL);
+    }
+  else
+    {
+      ClutterPoint cursor_position;
+
+      cursor_position = meta_cursor_renderer_get_position (cursor_renderer);
+      return clutter_rect_contains_point (&logical_monitor_rect,
+                                          &cursor_position);
+    }
+}
+
+static void
+sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src)
+{
+  MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src);
+  ClutterStage *stage = get_stage (monitor_src);
+
+  if (!is_cursor_in_stream (monitor_src))
+    return;
+
+  if (clutter_stage_is_redraw_queued (stage))
+    return;
+
+  meta_screen_cast_stream_src_maybe_record_frame (src);
+}
+
+static void
+cursor_moved (MetaCursorTracker              *cursor_tracker,
+              float                           x,
+              float                           y,
+              MetaScreenCastMonitorStreamSrc *monitor_src)
+{
+  sync_cursor_state (monitor_src);
+}
+
+static void
+cursor_changed (MetaCursorTracker              *cursor_tracker,
+                MetaScreenCastMonitorStreamSrc *monitor_src)
+{
+  sync_cursor_state (monitor_src);
+}
+
+static MetaCursorRenderer *
+get_cursor_renderer (MetaScreenCastMonitorStreamSrc *monitor_src)
+{
+  MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_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);
+  MetaBackend *backend = meta_screen_cast_get_backend (screen_cast);
+
+  return meta_backend_get_cursor_renderer (backend);
+}
+
+static void
+inhibit_hw_cursor (MetaScreenCastMonitorStreamSrc *monitor_src)
+{
+  MetaCursorRenderer *cursor_renderer;
+  MetaHwCursorInhibitor *inhibitor;
+
+  cursor_renderer = get_cursor_renderer (monitor_src);
+  inhibitor = META_HW_CURSOR_INHIBITOR (monitor_src);
+  meta_cursor_renderer_add_hw_cursor_inhibitor (cursor_renderer, inhibitor);
+}
+
+static void
+uninhibit_hw_cursor (MetaScreenCastMonitorStreamSrc *monitor_src)
+{
+  MetaCursorRenderer *cursor_renderer;
+  MetaHwCursorInhibitor *inhibitor;
+
+  cursor_renderer = get_cursor_renderer (monitor_src);
+  inhibitor = META_HW_CURSOR_INHIBITOR (monitor_src);
+  meta_cursor_renderer_remove_hw_cursor_inhibitor (cursor_renderer, inhibitor);
+}
+
 static void
 meta_screen_cast_monitor_stream_src_enable (MetaScreenCastStreamSrc *src)
 {
   MetaScreenCastMonitorStreamSrc *monitor_src =
     META_SCREEN_CAST_MONITOR_STREAM_SRC (src);
+  MetaBackend *backend = get_backend (monitor_src);
+  MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
   ClutterStage *stage;
+  MetaScreenCastStream *stream;
 
+  stream = meta_screen_cast_stream_src_get_stream (src);
   stage = get_stage (monitor_src);
-  monitor_src->actors_painted_handler_id =
-    g_signal_connect_after (stage, "actors-painted",
-                            G_CALLBACK (stage_painted),
-                            monitor_src);
+
+  switch (meta_screen_cast_stream_get_cursor_mode (stream))
+    {
+    case META_SCREEN_CAST_CURSOR_MODE_METADATA:
+      monitor_src->cursor_moved_handler_id =
+        g_signal_connect_after (cursor_tracker, "cursor-moved",
+                                G_CALLBACK (cursor_moved),
+                                monitor_src);
+      monitor_src->cursor_changed_handler_id =
+        g_signal_connect_after (cursor_tracker, "cursor-changed",
+                                G_CALLBACK (cursor_changed),
+                                monitor_src);
+      /* Intentional fall-through */
+    case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
+      monitor_src->actors_painted_handler_id =
+        g_signal_connect (stage, "actors-painted",
+                          G_CALLBACK (stage_painted),
+                          monitor_src);
+      break;
+    case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
+      inhibit_hw_cursor (monitor_src);
+      monitor_src->paint_handler_id =
+        g_signal_connect_after (stage, "paint",
+                                G_CALLBACK (stage_painted),
+                                monitor_src);
+      break;
+    }
+
   clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
 }
 
@@ -122,14 +280,43 @@ meta_screen_cast_monitor_stream_src_disable (MetaScreenCastStreamSrc *src)
 {
   MetaScreenCastMonitorStreamSrc *monitor_src =
     META_SCREEN_CAST_MONITOR_STREAM_SRC (src);
+  MetaBackend *backend = get_backend (monitor_src);
+  MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
   ClutterStage *stage;
 
   stage = get_stage (monitor_src);
-  g_signal_handler_disconnect (stage, monitor_src->actors_painted_handler_id);
-  monitor_src->actors_painted_handler_id = 0;
+
+  if (monitor_src->actors_painted_handler_id)
+    {
+      g_signal_handler_disconnect (stage,
+                                   monitor_src->actors_painted_handler_id);
+      monitor_src->actors_painted_handler_id = 0;
+    }
+
+  if (monitor_src->paint_handler_id)
+    {
+      g_signal_handler_disconnect (stage,
+                                   monitor_src->paint_handler_id);
+      monitor_src->paint_handler_id = 0;
+      uninhibit_hw_cursor (monitor_src);
+    }
+
+  if (monitor_src->cursor_moved_handler_id)
+    {
+      g_signal_handler_disconnect (cursor_tracker,
+                                   monitor_src->cursor_moved_handler_id);
+      monitor_src->cursor_moved_handler_id = 0;
+    }
+
+  if (monitor_src->cursor_changed_handler_id)
+    {
+      g_signal_handler_disconnect (cursor_tracker,
+                                   monitor_src->cursor_changed_handler_id);
+      monitor_src->cursor_changed_handler_id = 0;
+    }
 }
 
-static void
+static gboolean
 meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src,
                                                   uint8_t                 *data)
 {
@@ -140,9 +327,216 @@ meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src,
   MetaLogicalMonitor *logical_monitor;
 
   stage = get_stage (monitor_src);
+  if (!clutter_stage_is_redraw_queued (stage))
+    return FALSE;
+
   monitor = get_monitor (monitor_src);
   logical_monitor = meta_monitor_get_logical_monitor (monitor);
   clutter_stage_capture_into (stage, FALSE, &logical_monitor->rect, data);
+
+  return TRUE;
+}
+
+static gboolean
+draw_cursor_sprite_via_offscreen (MetaScreenCastMonitorStreamSrc  *monitor_src,
+                                  CoglTexture                     *cursor_texture,
+                                  int                              bitmap_width,
+                                  int                              bitmap_height,
+                                  uint32_t                        *bitmap_data,
+                                  GError                         **error)
+{
+  MetaBackend *backend = get_backend (monitor_src);
+  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
+  CoglContext *cogl_context =
+    clutter_backend_get_cogl_context (clutter_backend);
+  CoglTexture2D *bitmap_texture;
+  CoglOffscreen *offscreen;
+  CoglFramebuffer *fb;
+  CoglPipeline *pipeline;
+  CoglColor clear_color;
+
+  bitmap_texture = cogl_texture_2d_new_with_size (cogl_context,
+                                                  bitmap_width, bitmap_height);
+  cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (bitmap_texture),
+                                          FALSE);
+  if (!cogl_texture_allocate (COGL_TEXTURE (bitmap_texture), error))
+    {
+      cogl_object_unref (bitmap_texture);
+      return FALSE;
+    }
+
+  offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (bitmap_texture));
+  fb = COGL_FRAMEBUFFER (offscreen);
+  cogl_object_unref (bitmap_texture);
+  if (!cogl_framebuffer_allocate (fb, error))
+    {
+      cogl_object_unref (fb);
+      return FALSE;
+    }
+
+  pipeline = cogl_pipeline_new (cogl_context);
+  cogl_pipeline_set_layer_texture (pipeline, 0, cursor_texture);
+  cogl_pipeline_set_layer_filters (pipeline, 0,
+                                   COGL_PIPELINE_FILTER_LINEAR,
+                                   COGL_PIPELINE_FILTER_LINEAR);
+  cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0);
+  cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR, &clear_color);
+  cogl_framebuffer_draw_rectangle (fb, pipeline,
+                                   -1, 1, 1, -1);
+  cogl_object_unref (pipeline);
+
+  cogl_framebuffer_read_pixels (fb,
+                                0, 0,
+                                bitmap_width, bitmap_height,
+                                COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                                (uint8_t *) bitmap_data);
+  cogl_object_unref (fb);
+
+  return TRUE;
+}
+
+static void
+meta_screen_cast_monitor_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src,
+                                                         struct spa_meta_cursor  *spa_meta_cursor)
+{
+  MetaScreenCastMonitorStreamSrc *monitor_src =
+    META_SCREEN_CAST_MONITOR_STREAM_SRC (src);
+  MetaBackend *backend = get_backend (monitor_src);
+  MetaCursorRenderer *cursor_renderer =
+    meta_backend_get_cursor_renderer (backend);
+  MetaRenderer *renderer = meta_backend_get_renderer (backend);
+  MetaSpaType *spa_type = meta_screen_cast_stream_src_get_spa_type (src);
+  GError *error = NULL;
+  MetaCursorSprite *cursor_sprite;
+  CoglTexture *cursor_texture;
+  MetaMonitor *monitor;
+  MetaLogicalMonitor *logical_monitor;
+  MetaRectangle logical_monitor_layout;
+  ClutterRect logical_monitor_rect;
+  MetaRendererView *view;
+  float view_scale;
+  ClutterPoint cursor_position;
+  struct spa_meta_bitmap *spa_meta_bitmap;
+
+  cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer);
+  if (cursor_sprite)
+    cursor_texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite);
+  else
+    cursor_texture = NULL;
+
+  if (!is_cursor_in_stream (monitor_src))
+    {
+      spa_meta_cursor->id = 0;
+      return;
+    }
+
+  monitor = get_monitor (monitor_src);
+  logical_monitor = meta_monitor_get_logical_monitor (monitor);
+  logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor);
+  logical_monitor_rect =
+    meta_rectangle_to_clutter_rect (&logical_monitor_layout);
+
+  view = meta_renderer_get_view_from_logical_monitor (renderer,
+                                                      logical_monitor);
+  if (view)
+    view_scale = clutter_stage_view_get_scale (CLUTTER_STAGE_VIEW (view));
+  else
+    view_scale = 1.0;
+
+  cursor_position = meta_cursor_renderer_get_position (cursor_renderer);
+  cursor_position.x -= logical_monitor_rect.origin.x;
+  cursor_position.y -= logical_monitor_rect.origin.y;
+  cursor_position.x *= view_scale;
+  cursor_position.y *= view_scale;
+
+  spa_meta_cursor->id = 1;
+  spa_meta_cursor->position.x = (int32_t) roundf (cursor_position.x);
+  spa_meta_cursor->position.y = (int32_t) roundf (cursor_position.y);
+  spa_meta_cursor->bitmap_offset = sizeof (struct spa_meta_cursor);
+
+  spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor,
+                                spa_meta_cursor->bitmap_offset,
+                                struct spa_meta_bitmap);
+  spa_meta_bitmap->format = spa_type->video_format.RGBA;
+  spa_meta_bitmap->offset = sizeof (struct spa_meta_bitmap);
+
+  if (cursor_texture)
+    {
+      float cursor_scale;
+      float bitmap_scale;
+      int hotspot_x, hotspot_y;
+      int texture_width, texture_height;
+      int bitmap_width, bitmap_height;
+      uint32_t *bitmap_data;
+
+      cursor_scale = meta_cursor_sprite_get_texture_scale (cursor_sprite);
+      bitmap_scale = view_scale * cursor_scale;
+
+      meta_cursor_sprite_get_hotspot (cursor_sprite, &hotspot_x, &hotspot_y);
+      spa_meta_cursor->hotspot.x = (int32_t) roundf (hotspot_x * bitmap_scale);
+      spa_meta_cursor->hotspot.y = (int32_t) roundf (hotspot_y * bitmap_scale);
+
+      texture_width = cogl_texture_get_width (cursor_texture);
+      texture_height = cogl_texture_get_height (cursor_texture);
+      bitmap_width = texture_width * bitmap_scale;
+      bitmap_height = texture_height * bitmap_scale;
+
+      spa_meta_bitmap->size.width = bitmap_width;
+      spa_meta_bitmap->size.height = bitmap_height;
+      spa_meta_bitmap->stride = bitmap_width * 4;
+
+      bitmap_data = SPA_MEMBER (spa_meta_bitmap,
+                                spa_meta_bitmap->offset,
+                                uint32_t);
+
+      if (texture_width == bitmap_width &&
+          texture_height == bitmap_height)
+        {
+          cogl_texture_get_data (cursor_texture,
+                                 COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                                 texture_width * 4,
+                                 (uint8_t *) bitmap_data);
+        }
+      else
+        {
+          if (!draw_cursor_sprite_via_offscreen (monitor_src,
+                                                 cursor_texture,
+                                                 bitmap_width,
+                                                 bitmap_height,
+                                                 bitmap_data,
+                                                 &error))
+            {
+              g_warning ("Failed to draw cursor via offscreen: %s",
+                         error->message);
+              g_error_free (error);
+              spa_meta_cursor->id = 0;
+            }
+        }
+    }
+  else
+    {
+      spa_meta_cursor->hotspot.x = 0;
+      spa_meta_cursor->hotspot.y = 0;
+
+      *spa_meta_bitmap = (struct spa_meta_bitmap) { 0 };
+    }
+}
+
+static gboolean
+meta_screen_cast_monitor_stream_src_is_cursor_sprite_inhibited (MetaHwCursorInhibitor *inhibitor,
+                                                                MetaCursorSprite      *cursor_sprite)
+{
+  MetaScreenCastMonitorStreamSrc *monitor_src =
+    META_SCREEN_CAST_MONITOR_STREAM_SRC (inhibitor);
+
+  return is_cursor_in_stream (monitor_src);
+}
+
+static void
+hw_cursor_inhibitor_iface_init (MetaHwCursorInhibitorInterface *iface)
+{
+  iface->is_cursor_sprite_inhibited =
+    meta_screen_cast_monitor_stream_src_is_cursor_sprite_inhibited;
 }
 
 MetaScreenCastMonitorStreamSrc *
@@ -169,4 +563,6 @@ meta_screen_cast_monitor_stream_src_class_init (MetaScreenCastMonitorStreamSrcCl
   src_class->enable = meta_screen_cast_monitor_stream_src_enable;
   src_class->disable = meta_screen_cast_monitor_stream_src_disable;
   src_class->record_frame = meta_screen_cast_monitor_stream_src_record_frame;
+  src_class->set_cursor_metadata =
+    meta_screen_cast_monitor_stream_src_set_cursor_metadata;
 }
diff --git a/src/backends/meta-screen-cast-monitor-stream.c b/src/backends/meta-screen-cast-monitor-stream.c
index a6bed1b52..33b9f026a 100644
--- a/src/backends/meta-screen-cast-monitor-stream.c
+++ b/src/backends/meta-screen-cast-monitor-stream.c
@@ -105,11 +105,12 @@ meta_screen_cast_monitor_stream_get_monitor (MetaScreenCastMonitorStream *monito
 }
 
 MetaScreenCastMonitorStream *
-meta_screen_cast_monitor_stream_new (MetaScreenCastSession  *session,
-                                     GDBusConnection        *connection,
-                                     MetaMonitor            *monitor,
-                                     ClutterStage           *stage,
-                                     GError                **error)
+meta_screen_cast_monitor_stream_new (MetaScreenCastSession     *session,
+                                     GDBusConnection           *connection,
+                                     MetaMonitor               *monitor,
+                                     ClutterStage              *stage,
+                                     MetaScreenCastCursorMode   cursor_mode,
+                                     GError                   **error)
 {
   MetaGpu *gpu = meta_monitor_get_gpu (monitor);
   MetaMonitorManager *monitor_manager = meta_gpu_get_monitor_manager (gpu);
@@ -126,6 +127,7 @@ meta_screen_cast_monitor_stream_new (MetaScreenCastSession  *session,
                                    error,
                                    "session", session,
                                    "connection", connection,
+                                   "cursor-mode", cursor_mode,
                                    "monitor", monitor,
                                    NULL);
   if (!monitor_stream)
diff --git a/src/backends/meta-screen-cast-monitor-stream.h b/src/backends/meta-screen-cast-monitor-stream.h
index 98f160c88..f8dc04181 100644
--- a/src/backends/meta-screen-cast-monitor-stream.h
+++ b/src/backends/meta-screen-cast-monitor-stream.h
@@ -35,11 +35,12 @@ G_DECLARE_FINAL_TYPE (MetaScreenCastMonitorStream,
                       META, SCREEN_CAST_MONITOR_STREAM,
                       MetaScreenCastStream)
 
-MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (MetaScreenCastSession  *session,
-                                                                   GDBusConnection        *connection,
-                                                                   MetaMonitor            *monitor,
-                                                                   ClutterStage           *stage,
-                                                                   GError                **error);
+MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (MetaScreenCastSession     *session,
+                                                                   GDBusConnection           *connection,
+                                                                   MetaMonitor               *monitor,
+                                                                   ClutterStage              *stage,
+                                                                   MetaScreenCastCursorMode   cursor_mode,
+                                                                   GError                   **error);
 
 ClutterStage * meta_screen_cast_monitor_stream_get_stage (MetaScreenCastMonitorStream *monitor_stream);
 
diff --git a/src/backends/meta-screen-cast-session.c b/src/backends/meta-screen-cast-session.c
index 6a8f2d328..45d403dca 100644
--- a/src/backends/meta-screen-cast-session.c
+++ b/src/backends/meta-screen-cast-session.c
@@ -262,6 +262,20 @@ on_stream_closed (MetaScreenCastStream  *stream,
   meta_screen_cast_session_close (session);
 }
 
+static gboolean
+is_valid_cursor_mode (MetaScreenCastCursorMode cursor_mode)
+{
+  switch (cursor_mode)
+    {
+    case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
+    case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
+    case META_SCREEN_CAST_CURSOR_MODE_METADATA:
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
 static gboolean
 handle_record_monitor (MetaDBusScreenCastSession *skeleton,
                        GDBusMethodInvocation     *invocation,
@@ -275,6 +289,7 @@ handle_record_monitor (MetaDBusScreenCastSession *skeleton,
   MetaMonitorManager *monitor_manager =
     meta_backend_get_monitor_manager (backend);
   MetaMonitor *monitor;
+  MetaScreenCastCursorMode cursor_mode;
   ClutterStage *stage;
   GError *error = NULL;
   MetaScreenCastMonitorStream *monitor_stream;
@@ -306,12 +321,28 @@ handle_record_monitor (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;
+        }
+    }
+
   stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
 
   monitor_stream = meta_screen_cast_monitor_stream_new (session,
                                                         connection,
                                                         monitor,
                                                         stage,
+                                                        cursor_mode,
                                                         &error);
   if (!monitor_stream)
     {
diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c
index 04b84eb1d..7e11bd067 100644
--- a/src/backends/meta-screen-cast-stream-src.c
+++ b/src/backends/meta-screen-cast-stream-src.c
@@ -40,6 +40,10 @@
 #define PRIVATE_OWNER_FROM_FIELD(TypeName, field_ptr, field_name) \
   (TypeName *)((guint8 *)(field_ptr) - G_PRIVATE_OFFSET (TypeName, field_name))
 
+#define CURSOR_META_SIZE(width, height) \
+  (sizeof (struct spa_meta_cursor) + \
+   sizeof (struct spa_meta_bitmap) + width * height * 4)
+
 enum
 {
   PROP_0,
@@ -57,14 +61,6 @@ enum
 
 static guint signals[N_SIGNALS];
 
-typedef struct _MetaSpaType
-{
-  struct spa_type_media_type media_type;
-  struct spa_type_media_subtype media_subtype;
-  struct spa_type_format_video format_video;
-  struct spa_type_video_format video_format;
-} MetaSpaType;
-
 typedef struct _MetaPipeWireSource
 {
   GSource base;
@@ -133,14 +129,68 @@ meta_screen_cast_stream_src_get_videocrop (MetaScreenCastStreamSrc *src,
   return FALSE;
 }
 
-static void
+static gboolean
 meta_screen_cast_stream_src_record_frame (MetaScreenCastStreamSrc *src,
                                           uint8_t                 *data)
 {
   MetaScreenCastStreamSrcClass *klass =
     META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
 
-  klass->record_frame (src, data);
+  return klass->record_frame (src, data);
+}
+
+static void
+meta_screen_cast_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src,
+                                                 struct spa_meta_cursor  *spa_meta_cursor)
+{
+  MetaScreenCastStreamSrcClass *klass =
+    META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
+
+  if (klass->set_cursor_metadata)
+    klass->set_cursor_metadata (src, spa_meta_cursor);
+}
+
+MetaSpaType *
+meta_screen_cast_stream_src_get_spa_type (MetaScreenCastStreamSrc *src)
+{
+  MetaScreenCastStreamSrcPrivate *priv =
+    meta_screen_cast_stream_src_get_instance_private (src);
+
+  return &priv->spa_type;
+}
+
+static void
+add_cursor_metadata (MetaScreenCastStreamSrc *src,
+                     struct spa_buffer       *spa_buffer)
+{
+  MetaScreenCastStreamSrcPrivate *priv =
+    meta_screen_cast_stream_src_get_instance_private (src);
+  MetaSpaType *spa_type = &priv->spa_type;
+  struct spa_meta_cursor *spa_meta_cursor;
+
+  spa_meta_cursor = spa_buffer_find_meta (spa_buffer, spa_type->meta_cursor);
+  if (spa_meta_cursor)
+    meta_screen_cast_stream_src_set_cursor_metadata (src, spa_meta_cursor);
+}
+
+static void
+maybe_record_cursor (MetaScreenCastStreamSrc *src,
+                     struct spa_buffer       *spa_buffer,
+                     uint8_t                 *data)
+{
+  MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
+
+  switch (meta_screen_cast_stream_get_cursor_mode (stream))
+    {
+    case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
+    case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
+      return;
+    case META_SCREEN_CAST_CURSOR_MODE_METADATA:
+      add_cursor_metadata (src, spa_buffer);
+      return;
+    }
+
+  g_assert_not_reached ();
 }
 
 void
@@ -151,7 +201,6 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src)
   MetaRectangle crop_rect;
   struct pw_buffer *buffer;
   struct spa_buffer *spa_buffer;
-  struct spa_meta_video_crop *spa_meta_video_crop;
   uint8_t *map = NULL;
   uint8_t *data;
   uint64_t now_us;
@@ -199,35 +248,45 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src)
       return;
     }
 
-  meta_screen_cast_stream_src_record_frame (src, data);
-
-  /* Update VideoCrop if needed */
-  spa_meta_video_crop = spa_buffer_find_meta (spa_buffer, priv->pipewire_type->meta.VideoCrop);
-  if (spa_meta_video_crop)
+  if (meta_screen_cast_stream_src_record_frame (src, data))
     {
-      if (meta_screen_cast_stream_src_get_videocrop (src, &crop_rect))
-        {
-          spa_meta_video_crop->x = crop_rect.x;
-          spa_meta_video_crop->y = crop_rect.y;
-          spa_meta_video_crop->width = crop_rect.width;
-          spa_meta_video_crop->height = crop_rect.height;
-        }
-      else
+      struct spa_meta_video_crop *spa_meta_video_crop;
+
+      spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize;
+
+      /* Update VideoCrop if needed */
+      spa_meta_video_crop =
+        spa_buffer_find_meta (spa_buffer, priv->pipewire_type->meta.VideoCrop);
+      if (spa_meta_video_crop)
         {
-          spa_meta_video_crop->x = 0;
-          spa_meta_video_crop->y = 0;
-          spa_meta_video_crop->width = priv->stream_width;
-          spa_meta_video_crop->height = priv->stream_height;
+          if (meta_screen_cast_stream_src_get_videocrop (src, &crop_rect))
+            {
+              spa_meta_video_crop->x = crop_rect.x;
+              spa_meta_video_crop->y = crop_rect.y;
+              spa_meta_video_crop->width = crop_rect.width;
+              spa_meta_video_crop->height = crop_rect.height;
+            }
+          else
+            {
+              spa_meta_video_crop->x = 0;
+              spa_meta_video_crop->y = 0;
+              spa_meta_video_crop->width = priv->stream_width;
+              spa_meta_video_crop->height = priv->stream_height;
+            }
         }
     }
+  else
+    {
+      spa_buffer->datas[0].chunk->size = 0;
+    }
+
+  maybe_record_cursor (src, spa_buffer, data);
 
   priv->last_frame_timestamp_us = now_us;
 
   if (map)
     munmap (map, spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset);
 
-  spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize;
-
   pw_stream_queue_buffer (priv->pipewire_stream, buffer);
 }
 
@@ -314,7 +373,7 @@ on_stream_format_changed (void                 *data,
   uint8_t params_buffer[1024];
   int32_t width, height, stride, size;
   struct spa_pod_builder pod_builder;
-  const struct spa_pod *params[2];
+  const struct spa_pod *params[3];
   const int bpp = 4;
 
   if (!format)
@@ -348,6 +407,12 @@ on_stream_format_changed (void                 *data,
     ":", pipewire_type->param_meta.type, "I", pipewire_type->meta.VideoCrop,
     ":", pipewire_type->param_meta.size, "i", sizeof (struct spa_meta_video_crop));
 
+  params[2] = spa_pod_builder_object (
+    &pod_builder,
+    pipewire_type->param.idMeta, pipewire_type->param_meta.Meta,
+    ":", pipewire_type->param_meta.type, "I", priv->spa_type.meta_cursor,
+    ":", pipewire_type->param_meta.size, "i", CURSOR_META_SIZE (64, 64));
+
   pw_stream_finish_format (priv->pipewire_stream, 0,
                            params, G_N_ELEMENTS (params));
 }
@@ -517,6 +582,7 @@ init_spa_type (MetaSpaType         *type,
   spa_type_media_subtype_map (map, &type->media_subtype);
   spa_type_format_video_map (map, &type->format_video);
   spa_type_video_format_map (map, &type->video_format);
+  type->meta_cursor = spa_type_map_get_id(map, SPA_TYPE_META__Cursor);
 }
 
 static MetaPipeWireSource *
diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h
index f3b3fd779..f2f96f213 100644
--- a/src/backends/meta-screen-cast-stream-src.h
+++ b/src/backends/meta-screen-cast-stream-src.h
@@ -24,10 +24,26 @@
 #define META_SCREEN_CAST_STREAM_SRC_H
 
 #include <glib-object.h>
+#include <spa/param/video/format-utils.h>
+#include <spa/buffer/meta.h>
 
+#include "backends/meta-backend-private.h"
+#include "backends/meta-cursor-renderer.h"
+#include "backends/meta-cursor.h"
+#include "backends/meta-renderer.h"
 #include "clutter/clutter.h"
+#include "cogl/cogl.h"
 #include "meta/boxes.h"
 
+typedef struct _MetaSpaType
+{
+  struct spa_type_media_type media_type;
+  struct spa_type_media_subtype media_subtype;
+  struct spa_type_format_video format_video;
+  struct spa_type_video_format video_format;
+  uint32_t meta_cursor;
+} MetaSpaType;
+
 typedef struct _MetaScreenCastStream MetaScreenCastStream;
 
 #define META_TYPE_SCREEN_CAST_STREAM_SRC (meta_screen_cast_stream_src_get_type ())
@@ -46,14 +62,18 @@ struct _MetaScreenCastStreamSrcClass
                       float                   *frame_rate);
   void (* enable) (MetaScreenCastStreamSrc *src);
   void (* disable) (MetaScreenCastStreamSrc *src);
-  void (* record_frame) (MetaScreenCastStreamSrc *src,
-                         uint8_t                 *data);
+  gboolean (* record_frame) (MetaScreenCastStreamSrc *src,
+                             uint8_t                 *data);
   gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src,
                               MetaRectangle           *crop_rect);
+  void (* set_cursor_metadata) (MetaScreenCastStreamSrc *src,
+                                struct spa_meta_cursor  *spa_meta_cursor);
 };
 
 void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src);
 
 MetaScreenCastStream * meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src);
 
+MetaSpaType * meta_screen_cast_stream_src_get_spa_type (MetaScreenCastStreamSrc *src);
+
 #endif /* META_SCREEN_CAST_STREAM_SRC_H */
diff --git a/src/backends/meta-screen-cast-stream.c b/src/backends/meta-screen-cast-stream.c
index 875ada01a..c14f8fd85 100644
--- a/src/backends/meta-screen-cast-stream.c
+++ b/src/backends/meta-screen-cast-stream.c
@@ -34,6 +34,7 @@ enum
 
   PROP_SESSION,
   PROP_CONNECTION,
+  PROP_CURSOR_MODE,
 };
 
 enum
@@ -52,6 +53,8 @@ typedef struct _MetaScreenCastStreamPrivate
   GDBusConnection *connection;
   char *object_path;
 
+  MetaScreenCastCursorMode cursor_mode;
+
   MetaScreenCastStreamSrc *src;
 } MetaScreenCastStreamPrivate;
 
@@ -164,6 +167,15 @@ meta_screen_cast_stream_transform_position (MetaScreenCastStream *stream,
                                                                   y);
 }
 
+MetaScreenCastCursorMode
+meta_screen_cast_stream_get_cursor_mode (MetaScreenCastStream *stream)
+{
+  MetaScreenCastStreamPrivate *priv =
+    meta_screen_cast_stream_get_instance_private (stream);
+
+  return priv->cursor_mode;
+}
+
 static void
 meta_screen_cast_stream_set_property (GObject      *object,
                                       guint         prop_id,
@@ -182,6 +194,9 @@ meta_screen_cast_stream_set_property (GObject      *object,
     case PROP_CONNECTION:
       priv->connection = g_value_get_object (value);
       break;
+    case PROP_CURSOR_MODE:
+      priv->cursor_mode = g_value_get_uint (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -205,6 +220,9 @@ meta_screen_cast_stream_get_property (GObject    *object,
     case PROP_CONNECTION:
       g_value_set_object (value, priv->connection);
       break;
+    case PROP_CURSOR_MODE:
+      g_value_set_uint (value, priv->cursor_mode);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -296,6 +314,18 @@ meta_screen_cast_stream_class_init (MetaScreenCastStreamClass *klass)
                                                         G_PARAM_CONSTRUCT_ONLY |
                                                         G_PARAM_STATIC_STRINGS));
 
+  g_object_class_install_property (object_class,
+                                   PROP_CURSOR_MODE,
+                                   g_param_spec_uint ("cursor-mode",
+                                                      "cursor-mode",
+                                                      "Cursor mode",
+                                                      META_SCREEN_CAST_CURSOR_MODE_HIDDEN,
+                                                      META_SCREEN_CAST_CURSOR_MODE_METADATA,
+                                                      META_SCREEN_CAST_CURSOR_MODE_HIDDEN,
+                                                      G_PARAM_READWRITE |
+                                                      G_PARAM_CONSTRUCT_ONLY |
+                                                      G_PARAM_STATIC_STRINGS));
+
   signals[CLOSED] = g_signal_new ("closed",
                                   G_TYPE_FROM_CLASS (klass),
                                   G_SIGNAL_RUN_LAST,
diff --git a/src/backends/meta-screen-cast-stream.h b/src/backends/meta-screen-cast-stream.h
index d394dbb4d..4fc10869d 100644
--- a/src/backends/meta-screen-cast-stream.h
+++ b/src/backends/meta-screen-cast-stream.h
@@ -65,4 +65,6 @@ void meta_screen_cast_stream_transform_position (MetaScreenCastStream *stream,
                                                  double               *x,
                                                  double               *y);
 
+MetaScreenCastCursorMode meta_screen_cast_stream_get_cursor_mode (MetaScreenCastStream *stream);
+
 #endif /* META_SCREEN_CAST_STREAM_H */
diff --git a/src/backends/meta-screen-cast-window-stream-src.c 
b/src/backends/meta-screen-cast-window-stream-src.c
index c3f9cf5ca..32df9f127 100644
--- a/src/backends/meta-screen-cast-window-stream-src.c
+++ b/src/backends/meta-screen-cast-window-stream-src.c
@@ -207,7 +207,7 @@ meta_screen_cast_window_stream_src_disable (MetaScreenCastStreamSrc *src)
   meta_screen_cast_window_stream_src_stop (window_src);
 }
 
-static void
+static gboolean
 meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src,
                                                  uint8_t                 *data)
 {
@@ -215,6 +215,8 @@ meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src,
     META_SCREEN_CAST_WINDOW_STREAM_SRC (src);
 
   capture_into (window_src, data);
+
+  return TRUE;
 }
 
 MetaScreenCastWindowStreamSrc *
diff --git a/src/backends/meta-screen-cast.h b/src/backends/meta-screen-cast.h
index a5450ae0e..994c40c53 100644
--- a/src/backends/meta-screen-cast.h
+++ b/src/backends/meta-screen-cast.h
@@ -30,6 +30,13 @@
 
 #include "meta-dbus-screen-cast.h"
 
+typedef enum _MetaScreenCastCursorMode
+{
+  META_SCREEN_CAST_CURSOR_MODE_HIDDEN = 0,
+  META_SCREEN_CAST_CURSOR_MODE_EMBEDDED = 1,
+  META_SCREEN_CAST_CURSOR_MODE_METADATA = 2,
+} MetaScreenCastCursorMode;
+
 #define META_TYPE_SCREEN_CAST (meta_screen_cast_get_type ())
 G_DECLARE_FINAL_TYPE (MetaScreenCast, meta_screen_cast,
                       META, SCREEN_CAST,
diff --git a/src/org.gnome.Mutter.ScreenCast.xml b/src/org.gnome.Mutter.ScreenCast.xml
index 3cd02b6cb..953809727 100644
--- a/src/org.gnome.Mutter.ScreenCast.xml
+++ b/src/org.gnome.Mutter.ScreenCast.xml
@@ -71,7 +71,15 @@
 
        Record a single monitor.
 
-       Available @properties include: (none)
+       Available @properties include:
+
+       * "cursor-mode" (u): Cursor mode. Default: 'hidden' (see below)
+
+       Available cursor mode values:
+
+       0: hidden - cursor is not included in the stream
+       1: embedded - cursor is included in the framebuffer
+       2: metadata - cursor is included as metadata in the PipeWire stream
     -->
     <method name="RecordMonitor">
       <arg name="connector" type="s" direction="in" />
@@ -84,7 +92,7 @@
        @properties: Properties used determining what window to select
        @stream_path: Path to the new stream object
 
-       Record a single window.
+       Record a single window. The cursor will not be included.
 
        Available @properties include:
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]