[mutter/wip/texture-purge-on-nvidia: 64/71] screen-cast-cursor-side-channel.patch



commit e3a42dc8735e83d2df518b32edb9f2da4d338599
Author: Ray Strode <rstrode redhat com>
Date:   Mon Jan 14 10:06:59 2019 -0500

    screen-cast-cursor-side-channel.patch

 clutter/clutter/clutter-stage.c                    |  11 +
 clutter/clutter/clutter-stage.h                    |   3 +
 configure.ac                                       |   2 +-
 src/backends/meta-backend.c                        |   3 +-
 src/backends/meta-cursor-renderer.c                |  62 +++
 src/backends/meta-cursor-renderer.h                |  21 +
 src/backends/meta-cursor-tracker.c                 |  20 +-
 src/backends/meta-renderer.c                       |  18 +
 src/backends/meta-renderer.h                       |   3 +
 src/backends/meta-screen-cast-monitor-stream-src.c | 432 ++++++++++++++++++++-
 src/backends/meta-screen-cast-monitor-stream.c     |  15 +-
 src/backends/meta-screen-cast-monitor-stream.h     |  12 +-
 src/backends/meta-screen-cast-session.c            |  47 ++-
 src/backends/meta-screen-cast-session.h            |   2 +
 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             |  60 +++
 src/backends/meta-screen-cast-stream.h             |   8 +
 src/backends/meta-screen-cast-window-stream-src.c  |   4 +-
 src/backends/meta-screen-cast-window-stream.c      |   8 +-
 src/backends/meta-screen-cast-window-stream.h      |   7 +-
 src/backends/meta-screen-cast.c                    |  15 +-
 src/backends/meta-screen-cast.h                    |  13 +-
 src/backends/meta-stage.c                          |  21 +-
 src/backends/native/meta-cursor-renderer-native.c  |   4 +
 src/org.gnome.Mutter.ScreenCast.xml                |  12 +-
 26 files changed, 881 insertions(+), 74 deletions(-)
---
diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c
index 9352f49e8..763ec96ee 100644
--- a/clutter/clutter/clutter-stage.c
+++ b/clutter/clutter/clutter-stage.c
@@ -3722,6 +3722,17 @@ clutter_stage_ensure_redraw (ClutterStage *stage)
   _clutter_master_clock_start_running (master_clock);
 }
 
+/**
+ * clutter_stage_is_redraw_queued: (skip)
+ */
+gboolean
+clutter_stage_is_redraw_queued (ClutterStage *stage)
+{
+  ClutterStagePrivate *priv = stage->priv;
+
+  return priv->redraw_pending;
+}
+
 /**
  * clutter_stage_queue_redraw:
  * @stage: the #ClutterStage
diff --git a/clutter/clutter/clutter-stage.h b/clutter/clutter/clutter-stage.h
index e8fbda84c..ddf08fde4 100644
--- a/clutter/clutter/clutter-stage.h
+++ b/clutter/clutter/clutter-stage.h
@@ -250,6 +250,9 @@ void            clutter_stage_ensure_viewport                   (ClutterStage
 CLUTTER_AVAILABLE_IN_ALL
 void            clutter_stage_ensure_redraw                     (ClutterStage          *stage);
 
+CLUTTER_AVAILABLE_IN_ALL
+gboolean        clutter_stage_is_redraw_queued                  (ClutterStage          *stage);
+
 #ifdef CLUTTER_ENABLE_EXPERIMENTAL_API
 CLUTTER_AVAILABLE_IN_1_14
 void            clutter_stage_set_sync_delay                    (ClutterStage          *stage,
diff --git a/configure.ac b/configure.ac
index 2aa9c4352..449f3d693 100644
--- a/configure.ac
+++ b/configure.ac
@@ -245,7 +245,7 @@ AC_ARG_ENABLE(remote-desktop,
   enable_remote_desktop=no
 )
 AS_IF([test "$enable_remote_desktop" = "yes"], [
-  MUTTER_PC_MODULES="$MUTTER_PC_MODULES libpipewire-0.2 >= 0.2.2"
+  MUTTER_PC_MODULES="$MUTTER_PC_MODULES libpipewire-0.2 >= 0.2.5"
   AC_DEFINE([HAVE_REMOTE_DESKTOP],[1], [Defined if screen cast and remote desktop support is enabled])
 ])
 AM_CONDITIONAL([HAVE_REMOTE_DESKTOP],[test "$enable_remote_desktop" = "yes"])
diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c
index 888e51cd9..28f1cd92f 100644
--- a/src/backends/meta-backend.c
+++ b/src/backends/meta-backend.c
@@ -456,7 +456,8 @@ meta_backend_real_post_init (MetaBackend *backend)
   priv->remote_access_controller =
     g_object_new (META_TYPE_REMOTE_ACCESS_CONTROLLER, NULL);
   priv->dbus_session_watcher = g_object_new (META_TYPE_DBUS_SESSION_WATCHER, NULL);
-  priv->screen_cast = meta_screen_cast_new (priv->dbus_session_watcher);
+  priv->screen_cast = meta_screen_cast_new (backend,
+                                            priv->dbus_session_watcher);
   priv->remote_desktop = meta_remote_desktop_new (priv->dbus_session_watcher);
 #endif /* HAVE_REMOTE_DESKTOP */
 
diff --git a/src/backends/meta-cursor-renderer.c b/src/backends/meta-cursor-renderer.c
index eb79737f1..0a456bee6 100644
--- a/src/backends/meta-cursor-renderer.c
+++ b/src/backends/meta-cursor-renderer.c
@@ -35,6 +35,9 @@
 
 #include "meta-stage-private.h"
 
+G_DEFINE_INTERFACE (MetaHwCursorInhibitor, meta_hw_cursor_inhibitor,
+                    G_TYPE_OBJECT)
+
 struct _MetaCursorRendererPrivate
 {
   float current_x;
@@ -44,6 +47,8 @@ struct _MetaCursorRendererPrivate
   MetaOverlay *stage_overlay;
   gboolean handled_by_backend;
   guint post_paint_func_id;
+
+  GList *hw_cursor_inhibitors;
 };
 typedef struct _MetaCursorRendererPrivate MetaCursorRendererPrivate;
 
@@ -55,6 +60,21 @@ static guint signals[LAST_SIGNAL];
 
 G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRenderer, meta_cursor_renderer, G_TYPE_OBJECT);
 
+static gboolean
+meta_hw_cursor_inhibitor_is_cursor_sprite_inhibited (MetaHwCursorInhibitor *inhibitor,
+                                                     MetaCursorSprite      *cursor_sprite)
+{
+  MetaHwCursorInhibitorInterface *iface =
+    META_HW_CURSOR_INHIBITOR_GET_IFACE (inhibitor);
+
+  return iface->is_cursor_sprite_inhibited (inhibitor, cursor_sprite);
+}
+
+static void
+meta_hw_cursor_inhibitor_default_init (MetaHwCursorInhibitorInterface *iface)
+{
+}
+
 void
 meta_cursor_renderer_emit_painted (MetaCursorRenderer *renderer,
                                    MetaCursorSprite   *cursor_sprite)
@@ -283,3 +303,45 @@ meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer)
 
   return priv->displayed_cursor;
 }
+
+void
+meta_cursor_renderer_add_hw_cursor_inhibitor (MetaCursorRenderer    *renderer,
+                                              MetaHwCursorInhibitor *inhibitor)
+{
+  MetaCursorRendererPrivate *priv =
+    meta_cursor_renderer_get_instance_private (renderer);
+
+  priv->hw_cursor_inhibitors = g_list_prepend (priv->hw_cursor_inhibitors,
+                                               inhibitor);
+}
+
+void
+meta_cursor_renderer_remove_hw_cursor_inhibitor (MetaCursorRenderer    *renderer,
+                                                 MetaHwCursorInhibitor *inhibitor)
+{
+  MetaCursorRendererPrivate *priv =
+    meta_cursor_renderer_get_instance_private (renderer);
+
+  priv->hw_cursor_inhibitors = g_list_remove (priv->hw_cursor_inhibitors,
+                                              inhibitor);
+}
+
+gboolean
+meta_cursor_renderer_is_hw_cursors_inhibited (MetaCursorRenderer *renderer,
+                                              MetaCursorSprite   *cursor_sprite)
+{
+  MetaCursorRendererPrivate *priv =
+    meta_cursor_renderer_get_instance_private (renderer);
+  GList *l;
+
+  for (l = priv->hw_cursor_inhibitors; l; l = l->next)
+    {
+      MetaHwCursorInhibitor *inhibitor = l->data;
+
+      if (meta_hw_cursor_inhibitor_is_cursor_sprite_inhibited (inhibitor,
+                                                               cursor_sprite))
+        return TRUE;
+    }
+
+  return FALSE;
+}
diff --git a/src/backends/meta-cursor-renderer.h b/src/backends/meta-cursor-renderer.h
index 830d16ef6..092f17f1e 100644
--- a/src/backends/meta-cursor-renderer.h
+++ b/src/backends/meta-cursor-renderer.h
@@ -30,6 +30,18 @@
 #include <meta/screen.h>
 #include "meta-cursor.h"
 
+#define META_TYPE_HW_CURSOR_INHIBITOR (meta_hw_cursor_inhibitor_get_type ())
+G_DECLARE_INTERFACE (MetaHwCursorInhibitor, meta_hw_cursor_inhibitor,
+                     META, HW_CURSOR_INHIBITOR, GObject)
+
+struct _MetaHwCursorInhibitorInterface
+{
+  GTypeInterface parent_iface;
+
+  gboolean (* is_cursor_sprite_inhibited) (MetaHwCursorInhibitor *inhibitor,
+                                           MetaCursorSprite      *cursor_sprite);
+};
+
 #define META_TYPE_CURSOR_RENDERER (meta_cursor_renderer_get_type ())
 G_DECLARE_DERIVABLE_TYPE (MetaCursorRenderer, meta_cursor_renderer,
                           META, CURSOR_RENDERER, GObject);
@@ -55,6 +67,15 @@ void meta_cursor_renderer_force_update (MetaCursorRenderer *renderer);
 
 MetaCursorSprite * meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer);
 
+void meta_cursor_renderer_add_hw_cursor_inhibitor (MetaCursorRenderer    *renderer,
+                                                   MetaHwCursorInhibitor *inhibitor);
+
+void meta_cursor_renderer_remove_hw_cursor_inhibitor (MetaCursorRenderer    *renderer,
+                                                      MetaHwCursorInhibitor *inhibitor);
+
+gboolean meta_cursor_renderer_is_hw_cursors_inhibited (MetaCursorRenderer *renderer,
+                                                       MetaCursorSprite   *cursor_sprite);
+
 ClutterRect meta_cursor_renderer_calculate_rect (MetaCursorRenderer *renderer,
                                                  MetaCursorSprite   *cursor_sprite);
 
diff --git a/src/backends/meta-cursor-tracker.c b/src/backends/meta-cursor-tracker.c
index 6244f11ee..97e7f8cb4 100644
--- a/src/backends/meta-cursor-tracker.c
+++ b/src/backends/meta-cursor-tracker.c
@@ -48,6 +48,7 @@ G_DEFINE_TYPE (MetaCursorTracker, meta_cursor_tracker, G_TYPE_OBJECT);
 
 enum {
   CURSOR_CHANGED,
+  CURSOR_MOVED,
   LAST_SIGNAL
 };
 
@@ -117,11 +118,15 @@ change_cursor_renderer (MetaCursorTracker *tracker)
 static void
 sync_cursor (MetaCursorTracker *tracker)
 {
-  if (update_displayed_cursor (tracker))
-    g_signal_emit (tracker, signals[CURSOR_CHANGED], 0);
+  gboolean cursor_changed = FALSE;
+
+  cursor_changed = update_displayed_cursor (tracker);
 
   if (update_effective_cursor (tracker))
     change_cursor_renderer (tracker);
+
+  if (cursor_changed)
+    g_signal_emit (tracker, signals[CURSOR_CHANGED], 0);
 }
 
 static void
@@ -158,6 +163,15 @@ meta_cursor_tracker_class_init (MetaCursorTrackerClass *klass)
                                           0,
                                           NULL, NULL, NULL,
                                           G_TYPE_NONE, 0);
+
+  signals[CURSOR_MOVED] = g_signal_new ("cursor-moved",
+                                        G_TYPE_FROM_CLASS (klass),
+                                        G_SIGNAL_RUN_LAST,
+                                        0,
+                                        NULL, NULL, NULL,
+                                        G_TYPE_NONE, 2,
+                                        G_TYPE_FLOAT,
+                                        G_TYPE_FLOAT);
 }
 
 /**
@@ -334,6 +348,8 @@ meta_cursor_tracker_update_position (MetaCursorTracker *tracker,
   g_assert (meta_is_wayland_compositor ());
 
   meta_cursor_renderer_set_position (cursor_renderer, new_x, new_y);
+
+  g_signal_emit (tracker, signals[CURSOR_MOVED], 0, new_x, new_y);
 }
 
 static void
diff --git a/src/backends/meta-renderer.c b/src/backends/meta-renderer.c
index ceac7df57..ea93dc99e 100644
--- a/src/backends/meta-renderer.c
+++ b/src/backends/meta-renderer.c
@@ -94,6 +94,24 @@ meta_renderer_get_views (MetaRenderer *renderer)
   return priv->views;
 }
 
+MetaRendererView *
+meta_renderer_get_view_from_logical_monitor (MetaRenderer       *renderer,
+                                             MetaLogicalMonitor *logical_monitor)
+{
+  GList *l;
+
+  for (l = meta_renderer_get_views (renderer); l; l = l->next)
+    {
+      MetaRendererView *view = l->data;
+
+      if (meta_renderer_view_get_logical_monitor (view) ==
+          logical_monitor)
+        return view;
+    }
+
+  return NULL;
+}
+
 static void
 meta_renderer_finalize (GObject *object)
 {
diff --git a/src/backends/meta-renderer.h b/src/backends/meta-renderer.h
index bf51b51ab..1c617214b 100644
--- a/src/backends/meta-renderer.h
+++ b/src/backends/meta-renderer.h
@@ -53,4 +53,7 @@ void meta_renderer_set_legacy_view (MetaRenderer     *renderer,
 
 GList * meta_renderer_get_views (MetaRenderer *renderer);
 
+MetaRendererView * meta_renderer_get_view_from_logical_monitor (MetaRenderer       *renderer,
+                                                                MetaLogicalMonitor *logical_monitor);
+
 #endif /* META_RENDERER_H */
diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c 
b/src/backends/meta-screen-cast-monitor-stream-src.c
index 382d7d4a2..1d6aea242 100644
--- a/src/backends/meta-screen-cast-monitor-stream-src.c
+++ b/src/backends/meta-screen-cast-monitor-stream-src.c
@@ -24,23 +24,38 @@
 
 #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-screen-cast-monitor-stream.h"
+#include "backends/meta-screen-cast-session.h"
 #include "backends/meta-logical-monitor.h"
 #include "backends/meta-monitor.h"
 #include "clutter/clutter.h"
 #include "clutter/clutter-mutter.h"
+#include "core/boxes-private.h"
 
 struct _MetaScreenCastMonitorStreamSrc
 {
   MetaScreenCastStreamSrc parent;
 
-  gulong stage_painted_handler_id;
+  gboolean cursor_bitmap_invalid;
+
+  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 +117,164 @@ 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)
+{
+  monitor_src->cursor_bitmap_invalid = TRUE;
+  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->stage_painted_handler_id =
-    g_signal_connect_after (stage, "paint",
-                            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 +283,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->stage_painted_handler_id);
-  monitor_src->stage_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 +330,226 @@ 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);
+
+  if (!monitor_src->cursor_bitmap_invalid)
+    {
+      spa_meta_cursor->hotspot.x = 0;
+      spa_meta_cursor->hotspot.y = 0;
+      spa_meta_cursor->bitmap_offset = 0;
+      return;
+    }
+  monitor_src->cursor_bitmap_invalid = FALSE;
+
+  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 *
@@ -157,6 +564,7 @@ meta_screen_cast_monitor_stream_src_new (MetaScreenCastMonitorStream  *monitor_s
 static void
 meta_screen_cast_monitor_stream_src_init (MetaScreenCastMonitorStreamSrc *monitor_src)
 {
+  monitor_src->cursor_bitmap_invalid = TRUE;
 }
 
 static void
@@ -169,4 +577,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 df43f977c..33b9f026a 100644
--- a/src/backends/meta-screen-cast-monitor-stream.c
+++ b/src/backends/meta-screen-cast-monitor-stream.c
@@ -105,12 +105,15 @@ meta_screen_cast_monitor_stream_get_monitor (MetaScreenCastMonitorStream *monito
 }
 
 MetaScreenCastMonitorStream *
-meta_screen_cast_monitor_stream_new (GDBusConnection     *connection,
-                                     MetaMonitorManager  *monitor_manager,
-                                     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);
   MetaScreenCastMonitorStream *monitor_stream;
 
   if (!meta_monitor_is_active (monitor))
@@ -122,7 +125,9 @@ meta_screen_cast_monitor_stream_new (GDBusConnection     *connection,
   monitor_stream = g_initable_new (META_TYPE_SCREEN_CAST_MONITOR_STREAM,
                                    NULL,
                                    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 fbf3c77c3..f8dc04181 100644
--- a/src/backends/meta-screen-cast-monitor-stream.h
+++ b/src/backends/meta-screen-cast-monitor-stream.h
@@ -27,6 +27,7 @@
 
 #include "backends/meta-monitor-manager-private.h"
 #include "backends/meta-screen-cast-stream.h"
+#include "backends/meta-screen-cast.h"
 
 #define META_TYPE_SCREEN_CAST_MONITOR_STREAM (meta_screen_cast_monitor_stream_get_type ())
 G_DECLARE_FINAL_TYPE (MetaScreenCastMonitorStream,
@@ -34,11 +35,12 @@ G_DECLARE_FINAL_TYPE (MetaScreenCastMonitorStream,
                       META, SCREEN_CAST_MONITOR_STREAM,
                       MetaScreenCastStream)
 
-MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (GDBusConnection    *connection,
-                                                                  MetaMonitorManager *monitor_manager,
-                                                                   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 d0f5a79d9..45d403dca 100644
--- a/src/backends/meta-screen-cast-session.c
+++ b/src/backends/meta-screen-cast-session.c
@@ -38,6 +38,8 @@ struct _MetaScreenCastSession
 {
   MetaDBusScreenCastSessionSkeleton parent;
 
+  MetaScreenCast *screen_cast;
+
   char *peer_name;
 
   MetaScreenCastSessionType session_type;
@@ -159,6 +161,12 @@ meta_screen_cast_session_get_stream (MetaScreenCastSession *session,
   return NULL;
 }
 
+MetaScreenCast *
+meta_screen_cast_session_get_screen_cast (MetaScreenCastSession *session)
+{
+  return session->screen_cast;
+}
+
 char *
 meta_screen_cast_session_get_object_path (MetaScreenCastSession *session)
 {
@@ -254,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,
@@ -267,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;
@@ -298,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 (connection,
-                                                        monitor_manager,
+  monitor_stream = meta_screen_cast_monitor_stream_new (session,
+                                                        connection,
                                                         monitor,
                                                         stage,
+                                                        cursor_mode,
                                                         &error);
   if (!monitor_stream)
     {
@@ -382,7 +421,8 @@ handle_record_window (MetaDBusScreenCastSession *skeleton,
   interface_skeleton = G_DBUS_INTERFACE_SKELETON (skeleton);
   connection = g_dbus_interface_skeleton_get_connection (interface_skeleton);
 
-  window_stream = meta_screen_cast_window_stream_new (connection,
+  window_stream = meta_screen_cast_window_stream_new (session,
+                                                      connection,
                                                       window,
                                                       &error);
   if (!window_stream)
@@ -442,6 +482,7 @@ meta_screen_cast_session_new (MetaScreenCast             *screen_cast,
   static unsigned int global_session_number = 0;
 
   session = g_object_new (META_TYPE_SCREEN_CAST_SESSION, NULL);
+  session->screen_cast = screen_cast;
   session->session_type = session_type;
   session->peer_name = g_strdup (peer_name);
   session->object_path =
diff --git a/src/backends/meta-screen-cast-session.h b/src/backends/meta-screen-cast-session.h
index ac0a31a16..105a65098 100644
--- a/src/backends/meta-screen-cast-session.h
+++ b/src/backends/meta-screen-cast-session.h
@@ -60,4 +60,6 @@ void meta_screen_cast_session_close (MetaScreenCastSession *session);
 MetaScreenCastStream * meta_screen_cast_session_get_stream (MetaScreenCastSession *session,
                                                             const char            *path);
 
+MetaScreenCast * meta_screen_cast_session_get_screen_cast (MetaScreenCastSession *session);
+
 #endif /* META_SCREEN_CAST_SESSION_H */
diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c
index 673a4640b..9f97bf36d 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 97ee4bfcf..c14f8fd85 100644
--- a/src/backends/meta-screen-cast-stream.c
+++ b/src/backends/meta-screen-cast-stream.c
@@ -24,13 +24,17 @@
 
 #include "backends/meta-screen-cast-stream.h"
 
+#include "backends/meta-screen-cast-session.h"
+
 #define META_SCREEN_CAST_STREAM_DBUS_PATH "/org/gnome/Mutter/ScreenCast/Stream"
 
 enum
 {
   PROP_0,
 
+  PROP_SESSION,
   PROP_CONNECTION,
+  PROP_CURSOR_MODE,
 };
 
 enum
@@ -44,9 +48,13 @@ static guint signals[N_SIGNALS];
 
 typedef struct _MetaScreenCastStreamPrivate
 {
+  MetaScreenCastSession *session;
+
   GDBusConnection *connection;
   char *object_path;
 
+  MetaScreenCastCursorMode cursor_mode;
+
   MetaScreenCastStreamSrc *src;
 } MetaScreenCastStreamPrivate;
 
@@ -97,6 +105,15 @@ on_stream_src_ready (MetaScreenCastStreamSrc *src,
   meta_dbus_screen_cast_stream_emit_pipewire_stream_added (skeleton, node_id);
 }
 
+MetaScreenCastSession *
+meta_screen_cast_stream_get_session (MetaScreenCastStream *stream)
+{
+  MetaScreenCastStreamPrivate *priv =
+    meta_screen_cast_stream_get_instance_private (stream);
+
+  return priv->session;
+}
+
 gboolean
 meta_screen_cast_stream_start (MetaScreenCastStream  *stream,
                                GError               **error)
@@ -150,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,
@@ -162,9 +188,15 @@ meta_screen_cast_stream_set_property (GObject      *object,
 
   switch (prop_id)
     {
+    case PROP_SESSION:
+      priv->session = g_value_get_object (value);
+      break;
     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);
     }
@@ -182,9 +214,15 @@ meta_screen_cast_stream_get_property (GObject    *object,
 
   switch (prop_id)
     {
+    case PROP_SESSION:
+      g_value_set_object (value, priv->session);
+      break;
     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);
     }
@@ -256,6 +294,16 @@ meta_screen_cast_stream_class_init (MetaScreenCastStreamClass *klass)
   object_class->set_property = meta_screen_cast_stream_set_property;
   object_class->get_property = meta_screen_cast_stream_get_property;
 
+  g_object_class_install_property (object_class,
+                                   PROP_SESSION,
+                                   g_param_spec_object ("session",
+                                                        "session",
+                                                        "MetaScreenSession",
+                                                        META_TYPE_SCREEN_CAST_SESSION,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+
   g_object_class_install_property (object_class,
                                    PROP_CONNECTION,
                                    g_param_spec_object ("connection",
@@ -266,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 fd7930c4c..28ca6f683 100644
--- a/src/backends/meta-screen-cast-stream.h
+++ b/src/backends/meta-screen-cast-stream.h
@@ -26,8 +26,12 @@
 #include <glib-object.h>
 
 #include "backends/meta-screen-cast-stream-src.h"
+#include "backends/meta-screen-cast.h"
+
 #include "meta-dbus-screen-cast.h"
 
+typedef struct _MetaScreenCastSession MetaScreenCastSession;
+
 #define META_TYPE_SCREEN_CAST_STREAM (meta_screen_cast_stream_get_type ())
 G_DECLARE_DERIVABLE_TYPE (MetaScreenCastStream, meta_screen_cast_stream,
                           META, SCREEN_CAST_STREAM,
@@ -48,6 +52,8 @@ struct _MetaScreenCastStreamClass
                                double               *y);
 };
 
+MetaScreenCastSession * meta_screen_cast_stream_get_session (MetaScreenCastStream *stream);
+
 gboolean meta_screen_cast_stream_start (MetaScreenCastStream *stream,
                                         GError              **error);
 
@@ -61,4 +67,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-window-stream.c b/src/backends/meta-screen-cast-window-stream.c
index 1200a39ef..4c9227116 100644
--- a/src/backends/meta-screen-cast-window-stream.c
+++ b/src/backends/meta-screen-cast-window-stream.c
@@ -71,9 +71,10 @@ meta_screen_cast_window_stream_get_height (MetaScreenCastWindowStream *window_st
 }
 
 MetaScreenCastWindowStream *
-meta_screen_cast_window_stream_new (GDBusConnection  *connection,
-                                    MetaWindow       *window,
-                                    GError          **error)
+meta_screen_cast_window_stream_new (MetaScreenCastSession  *session,
+                                    GDBusConnection        *connection,
+                                    MetaWindow             *window,
+                                    GError                **error)
 {
   MetaScreenCastWindowStream *window_stream;
   MetaLogicalMonitor *logical_monitor;
@@ -90,6 +91,7 @@ meta_screen_cast_window_stream_new (GDBusConnection  *connection,
   window_stream = g_initable_new (META_TYPE_SCREEN_CAST_WINDOW_STREAM,
                                   NULL,
                                   error,
+                                  "session", session,
                                   "connection", connection,
                                   "window", window,
                                   NULL);
diff --git a/src/backends/meta-screen-cast-window-stream.h b/src/backends/meta-screen-cast-window-stream.h
index 6726ef873..3799be98a 100644
--- a/src/backends/meta-screen-cast-window-stream.h
+++ b/src/backends/meta-screen-cast-window-stream.h
@@ -32,9 +32,10 @@ G_DECLARE_FINAL_TYPE (MetaScreenCastWindowStream,
                       META, SCREEN_CAST_WINDOW_STREAM,
                       MetaScreenCastStream)
 
-MetaScreenCastWindowStream * meta_screen_cast_window_stream_new (GDBusConnection  *connection,
-                                                                 MetaWindow       *window,
-                                                                 GError          **error);
+MetaScreenCastWindowStream * meta_screen_cast_window_stream_new (MetaScreenCastSession  *session,
+                                                                 GDBusConnection        *connection,
+                                                                 MetaWindow             *window,
+                                                                 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/backends/meta-screen-cast.c b/src/backends/meta-screen-cast.c
index 4a67683e7..ceea9cbe1 100644
--- a/src/backends/meta-screen-cast.c
+++ b/src/backends/meta-screen-cast.c
@@ -43,6 +43,7 @@ struct _MetaScreenCast
   GList *sessions;
 
   MetaDbusSessionWatcher *session_watcher;
+  MetaBackend *backend;
 };
 
 static void
@@ -62,12 +63,20 @@ meta_screen_cast_get_connection (MetaScreenCast *screen_cast)
   return g_dbus_interface_skeleton_get_connection (interface_skeleton);
 }
 
+MetaBackend *
+meta_screen_cast_get_backend (MetaScreenCast *screen_cast)
+{
+  return screen_cast->backend;
+}
+
 static gboolean
 register_remote_desktop_screen_cast_session (MetaScreenCastSession  *session,
                                              const char             *remote_desktop_session_id,
                                              GError                **error)
 {
-  MetaBackend *backend = meta_get_backend ();
+  MetaScreenCast *screen_cast =
+    meta_screen_cast_session_get_screen_cast (session);
+  MetaBackend *backend = meta_screen_cast_get_backend (screen_cast);
   MetaRemoteDesktop *remote_desktop = meta_backend_get_remote_desktop (backend);
   MetaRemoteDesktopSession *remote_desktop_session;
 
@@ -244,11 +253,13 @@ meta_screen_cast_finalize (GObject *object)
 }
 
 MetaScreenCast *
-meta_screen_cast_new (MetaDbusSessionWatcher *session_watcher)
+meta_screen_cast_new (MetaBackend            *backend,
+                      MetaDbusSessionWatcher *session_watcher)
 {
   MetaScreenCast *screen_cast;
 
   screen_cast = g_object_new (META_TYPE_SCREEN_CAST, NULL);
+  screen_cast->backend = backend;
   screen_cast->session_watcher = session_watcher;
 
   return screen_cast;
diff --git a/src/backends/meta-screen-cast.h b/src/backends/meta-screen-cast.h
index a4fb8dd45..cadb6a2fe 100644
--- a/src/backends/meta-screen-cast.h
+++ b/src/backends/meta-screen-cast.h
@@ -25,9 +25,17 @@
 
 #include <glib-object.h>
 
+#include "backends/meta-backend-private.h"
 #include "backends/meta-dbus-session-watcher.h"
 #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,
@@ -35,6 +43,9 @@ G_DECLARE_FINAL_TYPE (MetaScreenCast, meta_screen_cast,
 
 GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *screen_cast);
 
-MetaScreenCast * meta_screen_cast_new (MetaDbusSessionWatcher *session_watcher);
+MetaBackend * meta_screen_cast_get_backend (MetaScreenCast *screen_cast);
+
+MetaScreenCast * meta_screen_cast_new (MetaBackend            *backend,
+                                       MetaDbusSessionWatcher *session_watcher);
 
 #endif /* META_SCREEN_CAST_H */
diff --git a/src/backends/meta-stage.c b/src/backends/meta-stage.c
index 73ca70118..4ed56c04b 100644
--- a/src/backends/meta-stage.c
+++ b/src/backends/meta-stage.c
@@ -30,7 +30,17 @@
 #include "backends/meta-backend-private.h"
 #include "clutter/clutter-mutter.h"
 
-struct _MetaOverlay {
+enum
+{
+  ACTORS_PAINTED,
+
+  N_SIGNALS
+};
+
+static guint signals[N_SIGNALS];
+
+struct _MetaOverlay
+{
   gboolean enabled;
 
   CoglPipeline *pipeline;
@@ -140,6 +150,8 @@ meta_stage_paint (ClutterActor *actor)
 
   CLUTTER_ACTOR_CLASS (meta_stage_parent_class)->paint (actor);
 
+  g_signal_emit (stage, signals[ACTORS_PAINTED], 0);
+
   for (l = priv->overlays; l; l = l->next)
     meta_overlay_paint (l->data);
 }
@@ -179,6 +191,13 @@ meta_stage_class_init (MetaStageClass *klass)
 
   stage_class->activate = meta_stage_activate;
   stage_class->deactivate = meta_stage_deactivate;
+
+  signals[ACTORS_PAINTED] = g_signal_new ("actors-painted",
+                                          G_TYPE_FROM_CLASS (klass),
+                                          G_SIGNAL_RUN_LAST,
+                                          0,
+                                          NULL, NULL, NULL,
+                                          G_TYPE_NONE, 0);
 }
 
 static void
diff --git a/src/backends/native/meta-cursor-renderer-native.c 
b/src/backends/native/meta-cursor-renderer-native.c
index 29800953b..3ff4e81fb 100644
--- a/src/backends/native/meta-cursor-renderer-native.c
+++ b/src/backends/native/meta-cursor-renderer-native.c
@@ -587,6 +587,10 @@ should_have_hw_cursor (MetaCursorRenderer *renderer,
   if (!cursor_sprite)
     return FALSE;
 
+  if (meta_cursor_renderer_is_hw_cursors_inhibited (renderer,
+                                                    cursor_sprite))
+    return FALSE;
+
   for (l = gpus; l; l = l->next)
     {
       MetaGpuKms *gpu_kms = l->data;
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]