[gnome-remote-desktop] session: Add methods to create streams on-the-fly



commit 47f90bdc2e71592da0bd72d90b9f05d087b3c29e
Author: Pascal Nowack <Pascal Nowack gmx de>
Date:   Sat Dec 4 15:35:59 2021 +0100

    session: Add methods to create streams on-the-fly
    
    This will be used for monitor configuration changes during a session
    and to unify both remote assistance and headless session paths.

 src/grd-session.c | 258 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 src/grd-session.h |  15 ++++
 2 files changed, 250 insertions(+), 23 deletions(-)
---
diff --git a/src/grd-session.c b/src/grd-session.c
index 41f72a02..74c20d6d 100644
--- a/src/grd-session.c
+++ b/src/grd-session.c
@@ -52,12 +52,20 @@ enum
 
 static guint signals[LAST_SIGNAL];
 
-typedef enum _GrdScreenCastCursorMode
+typedef enum _ScreenCastType
 {
-  GRD_SCREEN_CAST_CURSOR_MODE_HIDDEN = 0,
-  GRD_SCREEN_CAST_CURSOR_MODE_EMBEDDED = 1,
-  GRD_SCREEN_CAST_CURSOR_MODE_METADATA = 2,
-} GrdScreenCastCursorMode;
+  SCREEN_CAST_TYPE_NONE,
+  SCREEN_CAST_TYPE_MONITOR,
+  SCREEN_CAST_TYPE_WINDOW,
+  SCREEN_CAST_TYPE_AREA,
+  SCREEN_CAST_TYPE_VIRTUAL,
+} ScreenCastType;
+
+typedef struct _AsyncDBusRecordCallContext
+{
+  GrdSession *session;
+  ScreenCastType screen_cast_type;
+} AsyncDBusRecordCallContext;
 
 typedef struct _GrdSessionPrivate
 {
@@ -67,6 +75,7 @@ typedef struct _GrdSessionPrivate
   GrdDBusScreenCastSession *screen_cast_session;
 
   GrdStream *stream;
+  GHashTable *stream_table;
 
   GrdClipboard *clipboard;
 
@@ -92,6 +101,8 @@ void
 grd_session_stop (GrdSession *session)
 {
   GrdSessionPrivate *priv = grd_session_get_instance_private (session);
+  GHashTableIter iter;
+  GrdStream *stream;
 
   if (priv->cancellable && g_cancellable_is_cancelled (priv->cancellable))
     return;
@@ -113,6 +124,10 @@ grd_session_stop (GrdSession *session)
   if (priv->cancellable)
     g_cancellable_cancel (priv->cancellable);
 
+  g_hash_table_iter_init (&iter, priv->stream_table);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &stream))
+    grd_stream_disconnect_proxy_signals (stream);
+
   g_clear_signal_handler (&priv->caps_lock_state_changed_id,
                           priv->remote_desktop_session);
   g_clear_signal_handler (&priv->num_lock_state_changed_id,
@@ -127,6 +142,209 @@ grd_session_stop (GrdSession *session)
   g_signal_emit (session, signals[STOPPED], 0);
 }
 
+static void
+on_stream_ready (GrdStream  *stream,
+                 GrdSession *session)
+{
+  GRD_SESSION_GET_CLASS (session)->stream_ready (session, stream);
+}
+
+static void
+on_stream_closed (GrdStream  *stream,
+                  GrdSession *session)
+{
+  grd_session_stop (session);
+}
+
+static void
+on_screen_cast_stream_start_finished (GObject      *object,
+                                      GAsyncResult *result,
+                                      gpointer      user_data)
+{
+  GrdDBusScreenCastStream *stream_proxy = GRD_DBUS_SCREEN_CAST_STREAM (object);
+  g_autoptr (GError) error = NULL;
+
+  if (!grd_dbus_screen_cast_stream_call_start_finish (stream_proxy,
+                                                      result,
+                                                      &error))
+    {
+      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        return;
+
+      g_warning ("Failed to start screen cast stream: %s", error->message);
+      grd_session_stop (GRD_SESSION (user_data));
+      return;
+    }
+}
+
+static void
+on_screen_cast_stream_proxy_acquired (GObject      *object,
+                                      GAsyncResult *result,
+                                      gpointer      user_data)
+{
+  GrdDBusScreenCastStream *stream_proxy;
+  GrdSession *session;
+  GrdSessionPrivate *priv;
+  g_autoptr (GError) error = NULL;
+  GrdStream *stream;
+
+  stream_proxy = grd_dbus_screen_cast_stream_proxy_new_finish (result, &error);
+  if (!stream_proxy)
+    {
+      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        return;
+
+      g_warning ("Failed to acquire stream proxy: %s", error->message);
+      grd_session_stop (GRD_SESSION (user_data));
+      return;
+    }
+
+  session = GRD_SESSION (user_data);
+  priv = grd_session_get_instance_private (session);
+
+  stream = grd_stream_new (priv->context, stream_proxy);
+  g_signal_connect (stream, "ready", G_CALLBACK (on_stream_ready),
+                    session);
+  g_signal_connect (stream, "closed", G_CALLBACK (on_stream_closed),
+                    session);
+
+  g_hash_table_add (priv->stream_table, stream);
+
+  grd_dbus_screen_cast_stream_call_start (stream_proxy,
+                                          priv->cancellable,
+                                          on_screen_cast_stream_start_finished,
+                                          session);
+}
+
+static const char *
+screen_cast_type_to_string (ScreenCastType type)
+{
+  switch (type)
+    {
+    case SCREEN_CAST_TYPE_NONE:
+      return "none";
+    case SCREEN_CAST_TYPE_MONITOR:
+      return "monitor";
+    case SCREEN_CAST_TYPE_WINDOW:
+      return "window";
+    case SCREEN_CAST_TYPE_AREA:
+      return "area";
+    case SCREEN_CAST_TYPE_VIRTUAL:
+      return "virtual";
+    }
+
+  g_assert_not_reached ();
+}
+
+static void
+on_record_finished (GObject      *object,
+                    GAsyncResult *result,
+                    gpointer      user_data)
+{
+  GrdDBusScreenCastSession *proxy = GRD_DBUS_SCREEN_CAST_SESSION (object);
+  g_autofree AsyncDBusRecordCallContext *async_context = user_data;
+  ScreenCastType screen_cast_type = async_context->screen_cast_type;
+  GrdSession *session;
+  GrdSessionPrivate *priv;
+  GDBusConnection *connection;
+  g_autofree char *stream_path = NULL;
+  g_autoptr (GError) error = NULL;
+  gboolean retval = FALSE;
+
+  switch (screen_cast_type)
+    {
+    case SCREEN_CAST_TYPE_NONE:
+      g_assert_not_reached ();
+      break;
+    case SCREEN_CAST_TYPE_MONITOR:
+      retval = grd_dbus_screen_cast_session_call_record_monitor_finish (
+        proxy, &stream_path, result, &error);
+      break;
+    case SCREEN_CAST_TYPE_WINDOW:
+    case SCREEN_CAST_TYPE_AREA:
+      g_assert_not_reached ();
+      break;
+    case SCREEN_CAST_TYPE_VIRTUAL:
+      retval = grd_dbus_screen_cast_session_call_record_virtual_finish (
+        proxy, &stream_path, result, &error);
+      break;
+    }
+
+  if (!retval)
+    {
+      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        return;
+
+      g_warning ("Failed to record %s: %s",
+                 screen_cast_type_to_string (screen_cast_type), error->message);
+      grd_session_stop (async_context->session);
+      return;
+    }
+
+  session = async_context->session;
+  priv = grd_session_get_instance_private (session);
+  connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (proxy));
+
+  grd_dbus_screen_cast_stream_proxy_new (connection,
+                                         G_DBUS_PROXY_FLAGS_NONE,
+                                         MUTTER_SCREEN_CAST_BUS_NAME,
+                                         stream_path,
+                                         priv->cancellable,
+                                         on_screen_cast_stream_proxy_acquired,
+                                         session);
+}
+
+void
+grd_session_record_monitor (GrdSession              *session,
+                            const char              *connector,
+                            GrdScreenCastCursorMode  cursor_mode)
+{
+  GrdSessionPrivate *priv = grd_session_get_instance_private (session);
+  GVariantBuilder properties_builder;
+  AsyncDBusRecordCallContext *async_context;
+
+  async_context = g_malloc0 (sizeof (AsyncDBusRecordCallContext));
+  async_context->session = session;
+  async_context->screen_cast_type = SCREEN_CAST_TYPE_MONITOR;
+
+  g_variant_builder_init (&properties_builder, G_VARIANT_TYPE ("a{sv}"));
+  g_variant_builder_add (&properties_builder, "{sv}",
+                         "cursor-mode", g_variant_new_uint32 (cursor_mode));
+
+  grd_dbus_screen_cast_session_call_record_monitor (priv->screen_cast_session,
+                                                    connector ? connector : "",
+                                                    g_variant_builder_end (&properties_builder),
+                                                    priv->cancellable,
+                                                    on_record_finished,
+                                                    async_context);
+}
+
+void
+grd_session_record_virtual (GrdSession              *session,
+                            GrdScreenCastCursorMode  cursor_mode,
+                            gboolean                 is_platform)
+{
+  GrdSessionPrivate *priv = grd_session_get_instance_private (session);
+  GVariantBuilder properties_builder;
+  AsyncDBusRecordCallContext *async_context;
+
+  async_context = g_malloc0 (sizeof (AsyncDBusRecordCallContext));
+  async_context->session = session;
+  async_context->screen_cast_type = SCREEN_CAST_TYPE_VIRTUAL;
+
+  g_variant_builder_init (&properties_builder, G_VARIANT_TYPE ("a{sv}"));
+  g_variant_builder_add (&properties_builder, "{sv}",
+                         "cursor-mode", g_variant_new_uint32 (cursor_mode));
+  g_variant_builder_add (&properties_builder, "{sv}",
+                         "is-platform", g_variant_new_boolean (is_platform));
+
+  grd_dbus_screen_cast_session_call_record_virtual (priv->screen_cast_session,
+                                                    g_variant_builder_end (&properties_builder),
+                                                    priv->cancellable,
+                                                    on_record_finished,
+                                                    async_context);
+}
+
 void
 grd_session_notify_keyboard_keycode (GrdSession  *session,
                                      uint32_t     keycode,
@@ -427,20 +645,6 @@ grd_session_selection_read (GrdSession  *session,
   return fd;
 }
 
-static void
-on_stream_ready (GrdStream  *stream,
-                 GrdSession *session)
-{
-  GRD_SESSION_GET_CLASS (session)->stream_ready (session, stream);
-}
-
-static void
-on_stream_closed (GrdStream  *stream,
-                  GrdSession *session)
-{
-  grd_session_stop (session);
-}
-
 static void
 on_session_start_finished (GObject      *object,
                            GAsyncResult *result,
@@ -488,9 +692,9 @@ start_session (GrdSession *session)
 }
 
 static void
-on_screen_cast_stream_proxy_acquired (GObject      *object,
-                                      GAsyncResult *result,
-                                      gpointer      user_data)
+on_screen_cast_stream_proxy_acquired_ex (GObject      *object,
+                                         GAsyncResult *result,
+                                         gpointer      user_data)
 {
   GrdDBusScreenCastStream *stream_proxy;
   GrdSession *session;
@@ -557,7 +761,7 @@ on_record_monitor_finished (GObject      *object,
                                          MUTTER_SCREEN_CAST_BUS_NAME,
                                          stream_path,
                                          priv->cancellable,
-                                         on_screen_cast_stream_proxy_acquired,
+                                         on_screen_cast_stream_proxy_acquired_ex,
                                          session);
 }
 
@@ -897,6 +1101,8 @@ grd_session_dispose (GObject *object)
 
   g_clear_object (&priv->stream);
 
+  g_hash_table_remove_all (priv->stream_table);
+
   G_OBJECT_CLASS (grd_session_parent_class)->dispose (object);
 }
 
@@ -912,6 +1118,9 @@ grd_session_finalize (GObject *object)
     g_assert (g_cancellable_is_cancelled (priv->cancellable));
   g_clear_object (&priv->cancellable);
 
+  g_assert (g_hash_table_size (priv->stream_table) == 0);
+  g_clear_pointer (&priv->stream_table, g_hash_table_unref);
+
   G_OBJECT_CLASS (grd_session_parent_class)->finalize (object);
 }
 
@@ -957,6 +1166,9 @@ grd_session_get_property (GObject    *object,
 static void
 grd_session_init (GrdSession *session)
 {
+  GrdSessionPrivate *priv = grd_session_get_instance_private (session);
+
+  priv->stream_table = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
 }
 
 static void
diff --git a/src/grd-session.h b/src/grd-session.h
index f0cdc2f1..519deee5 100644
--- a/src/grd-session.h
+++ b/src/grd-session.h
@@ -33,6 +33,13 @@
 #define GRD_TYPE_SESSION (grd_session_get_type ())
 G_DECLARE_DERIVABLE_TYPE (GrdSession, grd_session, GRD, SESSION, GObject)
 
+typedef enum _GrdScreenCastCursorMode
+{
+  GRD_SCREEN_CAST_CURSOR_MODE_HIDDEN = 0,
+  GRD_SCREEN_CAST_CURSOR_MODE_EMBEDDED = 1,
+  GRD_SCREEN_CAST_CURSOR_MODE_METADATA = 2,
+} GrdScreenCastCursorMode;
+
 typedef enum _GrdKeyState
 {
   GRD_KEY_STATE_RELEASED,
@@ -77,6 +84,14 @@ struct _GrdSessionClass
 
 GrdContext *grd_session_get_context (GrdSession *session);
 
+void grd_session_record_monitor (GrdSession              *session,
+                                 const char              *connector,
+                                 GrdScreenCastCursorMode  cursor_mode);
+
+void grd_session_record_virtual (GrdSession              *session,
+                                 GrdScreenCastCursorMode  cursor_mode,
+                                 gboolean                 is_platform);
+
 void grd_session_notify_keyboard_keycode (GrdSession  *session,
                                           uint32_t     keycode,
                                           GrdKeyState  state);


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