kathenas / rpms / mutter

Forked from rpms/mutter 5 years ago
Clone
Blob Blame History Raw
From 5f428c5aa9f3e66cd23e4e75d54506960bd5b66c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Wed, 12 Dec 2018 11:27:16 +0100
Subject: [PATCH 01/12] cursor-tracker: Add 'cursor-moved' signal

https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
---
 src/backends/meta-cursor-tracker.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/backends/meta-cursor-tracker.c b/src/backends/meta-cursor-tracker.c
index 6244f11ee..060b6af3b 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
 };
 
@@ -158,6 +159,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 +344,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
-- 
2.20.1


From 54c5034f6dcdaf382a8355460710ced47c05d91e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Fri, 21 Dec 2018 17:28:33 +0100
Subject: [PATCH 02/12] clutter/stage: Add clutter_stage_is_redraw_queued() API

This will be used by the screen casting code to check whether it should
wait for a frame before reading cursor state, or send only the cursor
update, if no redraw is queued.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
---
 clutter/clutter/clutter-stage.c | 11 +++++++++++
 clutter/clutter/clutter-stage.h |  3 +++
 2 files changed, 14 insertions(+)

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,
-- 
2.20.1


From 59e1c68143d9090e238198448fa400bb0776d38a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Wed, 12 Dec 2018 11:32:11 +0100
Subject: [PATCH 03/12] screen-cast-monitor-stream: Don't pass monitor manager
 when creating

It can be fetched indirectly from the monitor already.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
---
 src/backends/meta-screen-cast-monitor-stream.c | 11 ++++++-----
 src/backends/meta-screen-cast-monitor-stream.h |  9 ++++-----
 src/backends/meta-screen-cast-session.c        |  1 -
 3 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/src/backends/meta-screen-cast-monitor-stream.c b/src/backends/meta-screen-cast-monitor-stream.c
index df43f977c..5a816e4df 100644
--- a/src/backends/meta-screen-cast-monitor-stream.c
+++ b/src/backends/meta-screen-cast-monitor-stream.c
@@ -105,12 +105,13 @@ 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 (GDBusConnection  *connection,
+                                     MetaMonitor      *monitor,
+                                     ClutterStage     *stage,
+                                     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))
diff --git a/src/backends/meta-screen-cast-monitor-stream.h b/src/backends/meta-screen-cast-monitor-stream.h
index fbf3c77c3..1d24de93a 100644
--- a/src/backends/meta-screen-cast-monitor-stream.h
+++ b/src/backends/meta-screen-cast-monitor-stream.h
@@ -34,11 +34,10 @@ 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 (GDBusConnection  *connection,
+                                                                   MetaMonitor      *monitor,
+                                                                   ClutterStage     *stage,
+                                                                   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..3ba59037f 100644
--- a/src/backends/meta-screen-cast-session.c
+++ b/src/backends/meta-screen-cast-session.c
@@ -301,7 +301,6 @@ handle_record_monitor (MetaDBusScreenCastSession *skeleton,
   stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
 
   monitor_stream = meta_screen_cast_monitor_stream_new (connection,
-                                                        monitor_manager,
                                                         monitor,
                                                         stage,
                                                         &error);
-- 
2.20.1


From 45100944033987a946672fda6de309aa41a6139f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Wed, 12 Dec 2018 11:35:58 +0100
Subject: [PATCH 04/12] screen-cast: Add getters to fetch object owners

MetaBackend owns MetaScreenCast which owns MetaScreenCastSession which
owns MetaScreenCastStream. Make it possible to fetch objects in the
oppositev direction too.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
---
 src/backends/meta-backend.c                   |  3 +-
 .../meta-screen-cast-monitor-stream.c         | 10 ++++---
 .../meta-screen-cast-monitor-stream.h         | 10 ++++---
 src/backends/meta-screen-cast-session.c       | 15 ++++++++--
 src/backends/meta-screen-cast-session.h       |  2 ++
 src/backends/meta-screen-cast-stream.c        | 30 +++++++++++++++++++
 src/backends/meta-screen-cast-stream.h        |  6 ++++
 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               |  6 +++-
 11 files changed, 92 insertions(+), 20 deletions(-)

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-screen-cast-monitor-stream.c b/src/backends/meta-screen-cast-monitor-stream.c
index 5a816e4df..a6bed1b52 100644
--- a/src/backends/meta-screen-cast-monitor-stream.c
+++ b/src/backends/meta-screen-cast-monitor-stream.c
@@ -105,10 +105,11 @@ meta_screen_cast_monitor_stream_get_monitor (MetaScreenCastMonitorStream *monito
 }
 
 MetaScreenCastMonitorStream *
-meta_screen_cast_monitor_stream_new (GDBusConnection  *connection,
-                                     MetaMonitor      *monitor,
-                                     ClutterStage     *stage,
-                                     GError          **error)
+meta_screen_cast_monitor_stream_new (MetaScreenCastSession  *session,
+                                     GDBusConnection        *connection,
+                                     MetaMonitor            *monitor,
+                                     ClutterStage           *stage,
+                                     GError                **error)
 {
   MetaGpu *gpu = meta_monitor_get_gpu (monitor);
   MetaMonitorManager *monitor_manager = meta_gpu_get_monitor_manager (gpu);
@@ -123,6 +124,7 @@ 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,
                                    "monitor", monitor,
                                    NULL);
diff --git a/src/backends/meta-screen-cast-monitor-stream.h b/src/backends/meta-screen-cast-monitor-stream.h
index 1d24de93a..98f160c88 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,10 +35,11 @@ G_DECLARE_FINAL_TYPE (MetaScreenCastMonitorStream,
                       META, SCREEN_CAST_MONITOR_STREAM,
                       MetaScreenCastStream)
 
-MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (GDBusConnection  *connection,
-                                                                   MetaMonitor      *monitor,
-                                                                   ClutterStage     *stage,
-                                                                   GError          **error);
+MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (MetaScreenCastSession  *session,
+                                                                   GDBusConnection        *connection,
+                                                                   MetaMonitor            *monitor,
+                                                                   ClutterStage           *stage,
+                                                                   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 3ba59037f..6a8f2d328 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)
 {
@@ -300,7 +308,8 @@ handle_record_monitor (MetaDBusScreenCastSession *skeleton,
 
   stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
 
-  monitor_stream = meta_screen_cast_monitor_stream_new (connection,
+  monitor_stream = meta_screen_cast_monitor_stream_new (session,
+                                                        connection,
                                                         monitor,
                                                         stage,
                                                         &error);
@@ -381,7 +390,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)
@@ -441,6 +451,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.c b/src/backends/meta-screen-cast-stream.c
index 97ee4bfcf..875ada01a 100644
--- a/src/backends/meta-screen-cast-stream.c
+++ b/src/backends/meta-screen-cast-stream.c
@@ -24,12 +24,15 @@
 
 #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,
 };
 
@@ -44,6 +47,8 @@ static guint signals[N_SIGNALS];
 
 typedef struct _MetaScreenCastStreamPrivate
 {
+  MetaScreenCastSession *session;
+
   GDBusConnection *connection;
   char *object_path;
 
@@ -97,6 +102,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)
@@ -162,6 +176,9 @@ 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;
@@ -182,6 +199,9 @@ 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;
@@ -256,6 +276,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",
diff --git a/src/backends/meta-screen-cast-stream.h b/src/backends/meta-screen-cast-stream.h
index fd7930c4c..dcc280da6 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);
 
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..7e32b67b7 100644
--- a/src/backends/meta-screen-cast.h
+++ b/src/backends/meta-screen-cast.h
@@ -25,6 +25,7 @@
 
 #include <glib-object.h>
 
+#include "backends/meta-backend-private.h"
 #include "backends/meta-dbus-session-watcher.h"
 #include "meta-dbus-screen-cast.h"
 
@@ -35,6 +36,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 */
-- 
2.20.1


From 0bda3f694c9ea14239dd002856302e7d3007ae1c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Wed, 12 Dec 2018 11:37:13 +0100
Subject: [PATCH 05/12] renderer: Add API to get view from logical monitor

Will be used to get the view scale for a logical monitor, which is
necessary for passing cursor sprites via PipeWire.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
---
 src/backends/meta-renderer.c | 18 ++++++++++++++++++
 src/backends/meta-renderer.h |  3 +++
 2 files changed, 21 insertions(+)

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 */
-- 
2.20.1


From 1a928d060cccd344004a06dacc008ae0654e8f3e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Wed, 12 Dec 2018 11:39:00 +0100
Subject: [PATCH 06/12] backends/stage: Fix minor style issue

https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
---
 src/backends/meta-stage.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/backends/meta-stage.c b/src/backends/meta-stage.c
index 73ca70118..d1b7185e8 100644
--- a/src/backends/meta-stage.c
+++ b/src/backends/meta-stage.c
@@ -30,7 +30,8 @@
 #include "backends/meta-backend-private.h"
 #include "clutter/clutter-mutter.h"
 
-struct _MetaOverlay {
+struct _MetaOverlay
+{
   gboolean enabled;
 
   CoglPipeline *pipeline;
-- 
2.20.1


From 44655d12468c26bf6949e4b5137d324f9ec3b651 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Wed, 12 Dec 2018 11:39:18 +0100
Subject: [PATCH 07/12] backends/stage: Emit signal between painting actors and
 overlays

Will be used by screen casting for embedding the cursor separately, or
not including at all.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
---
 src/backends/meta-stage.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/src/backends/meta-stage.c b/src/backends/meta-stage.c
index d1b7185e8..4ed56c04b 100644
--- a/src/backends/meta-stage.c
+++ b/src/backends/meta-stage.c
@@ -30,6 +30,15 @@
 #include "backends/meta-backend-private.h"
 #include "clutter/clutter-mutter.h"
 
+enum
+{
+  ACTORS_PAINTED,
+
+  N_SIGNALS
+};
+
+static guint signals[N_SIGNALS];
+
 struct _MetaOverlay
 {
   gboolean enabled;
@@ -141,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);
 }
@@ -180,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
-- 
2.20.1


From 6afa7372f3c8104fb5670130b1cd54980e9b6446 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Wed, 12 Dec 2018 15:29:21 +0100
Subject: [PATCH 08/12] screen-cast/monitor-stream-src: Copy content before
 cursor is drawn

To get a consistent behaviour no matter whether HW cursors are in use or
not, make sure to copy the framebuffer content before the stage overlays
(cursor sprite textures) are painted.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
---
 src/backends/meta-screen-cast-monitor-stream-src.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c
index 382d7d4a2..b240744a8 100644
--- a/src/backends/meta-screen-cast-monitor-stream-src.c
+++ b/src/backends/meta-screen-cast-monitor-stream-src.c
@@ -35,7 +35,7 @@ struct _MetaScreenCastMonitorStreamSrc
 {
   MetaScreenCastStreamSrc parent;
 
-  gulong stage_painted_handler_id;
+  gulong actors_painted_handler_id;
 };
 
 G_DEFINE_TYPE (MetaScreenCastMonitorStreamSrc,
@@ -110,8 +110,8 @@ meta_screen_cast_monitor_stream_src_enable (MetaScreenCastStreamSrc *src)
   ClutterStage *stage;
 
   stage = get_stage (monitor_src);
-  monitor_src->stage_painted_handler_id =
-    g_signal_connect_after (stage, "paint",
+  monitor_src->actors_painted_handler_id =
+    g_signal_connect_after (stage, "actors-painted",
                             G_CALLBACK (stage_painted),
                             monitor_src);
   clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
@@ -125,8 +125,8 @@ meta_screen_cast_monitor_stream_src_disable (MetaScreenCastStreamSrc *src)
   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;
+  g_signal_handler_disconnect (stage, monitor_src->actors_painted_handler_id);
+  monitor_src->actors_painted_handler_id = 0;
 }
 
 static void
-- 
2.20.1


From 2e03a842ce2447f9d070a410d1a80c2e77b480a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Thu, 13 Sep 2018 11:28:51 +0200
Subject: [PATCH 09/12] cursor-renderer: Add API to allow inhibiting HW cursor

There may be reasons to temporarly inhibit the HW cursor under certain
circumstances. Allow adding such inhibitations by adding API to the
cursor renderer to allow API users to add generic inhibitors with
whatever logic is deemed necessary.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
---
 src/backends/meta-cursor-renderer.c           | 62 +++++++++++++++++++
 src/backends/meta-cursor-renderer.h           | 21 +++++++
 .../native/meta-cursor-renderer-native.c      |  4 ++
 3 files changed, 87 insertions(+)

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/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;
-- 
2.20.1


From 65eaf0c6a12ca48fc92fbe4492726cd145549924 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Fri, 14 Dec 2018 17:47:57 +0100
Subject: [PATCH 10/12] 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
---
 configure.ac                                  |   2 +-
 .../meta-screen-cast-monitor-stream-src.c     | 416 +++++++++++++++++-
 .../meta-screen-cast-monitor-stream.c         |  12 +-
 .../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 +
 .../meta-screen-cast-window-stream-src.c      |   4 +-
 src/backends/meta-screen-cast.h               |   7 +
 src/org.gnome.Mutter.ScreenCast.xml           |  12 +-
 12 files changed, 622 insertions(+), 57 deletions(-)

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-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c
index b240744a8..6be477989 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-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 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 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 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 dcc280da6..28ca6f683 100644
--- a/src/backends/meta-screen-cast-stream.h
+++ b/src/backends/meta-screen-cast-stream.h
@@ -67,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.h b/src/backends/meta-screen-cast.h
index 7e32b67b7..cadb6a2fe 100644
--- a/src/backends/meta-screen-cast.h
+++ b/src/backends/meta-screen-cast.h
@@ -29,6 +29,13 @@
 #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,
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:
 
-- 
2.20.1


From 93c2b18337a8babe340775cd979ad540f0838ab3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Thu, 3 Jan 2019 16:40:42 +0100
Subject: [PATCH 11/12] cursor-tracker: Emit `cursor-changed` after renderer
 was updated

Otherwise the cursor retrieved via meta_cursor_renderer_get_cursor() is
out of date.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
---
 src/backends/meta-cursor-tracker.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/backends/meta-cursor-tracker.c b/src/backends/meta-cursor-tracker.c
index 060b6af3b..97e7f8cb4 100644
--- a/src/backends/meta-cursor-tracker.c
+++ b/src/backends/meta-cursor-tracker.c
@@ -118,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
-- 
2.20.1


From 392205b1d0728ac1636df90144c0fe091bd45624 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Thu, 3 Jan 2019 16:51:08 +0100
Subject: [PATCH 12/12] screen-cast-monitor-stream-src: Only send cursor bitmap
 when it changes

To avoid unnecessary pixel copying, only send the cursor bitmap when it
changes. This also allows the receiver to know when the cursor bitmap
actually changed.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
---
 src/backends/meta-screen-cast-monitor-stream-src.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c
index 6be477989..1d6aea242 100644
--- a/src/backends/meta-screen-cast-monitor-stream-src.c
+++ b/src/backends/meta-screen-cast-monitor-stream-src.c
@@ -40,6 +40,8 @@ struct _MetaScreenCastMonitorStreamSrc
 {
   MetaScreenCastStreamSrc parent;
 
+  gboolean cursor_bitmap_invalid;
+
   gulong actors_painted_handler_id;
   gulong paint_handler_id;
   gulong cursor_moved_handler_id;
@@ -194,6 +196,7 @@ static void
 cursor_changed (MetaCursorTracker              *cursor_tracker,
                 MetaScreenCastMonitorStreamSrc *monitor_src)
 {
+  monitor_src->cursor_bitmap_invalid = TRUE;
   sync_cursor_state (monitor_src);
 }
 
@@ -452,6 +455,16 @@ meta_screen_cast_monitor_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc
   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,
@@ -551,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
-- 
2.20.1