[gnome-remote-desktop] session-rdp: Add API to subscribe to DVC creation statuses



commit a15d73d0bdb7523b3f6bbe2b952f7a9c764ba692
Author: Pascal Nowack <Pascal Nowack gmx de>
Date:   Tue Jun 7 08:36:36 2022 +0200

    session-rdp: Add API to subscribe to DVC creation statuses
    
    Opening a dynamic virtual channel (DVC) is not an instant action.
    Instead, the server side sends a DVC Create Request PDU
    (DYNVC_CREATE_REQ) and the client side answer with a DVC Create
    Response PDU (DYNVC_CREATE_RSP).
    Only if the creation status value is non-negative, the creation was
    successful.
    With the latest FreeRDP stable update, it is now possible to get
    notified of that creation status.
    
    So, use that API in FreeRDP to add an API in gnome-remote-desktop to
    allow DVCs to subscribe to their creation status.
    This API will later be used by DVCs implemented in
    gnome-remote-desktop.

 src/grd-session-rdp.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/grd-session-rdp.h |  12 +++
 2 files changed, 252 insertions(+)
---
diff --git a/src/grd-session-rdp.c b/src/grd-session-rdp.c
index 63e0bfc5..26a4df57 100644
--- a/src/grd-session-rdp.c
+++ b/src/grd-session-rdp.c
@@ -74,6 +74,23 @@ typedef enum _PauseKeyState
   PAUSE_KEY_STATE_CTRL_UP,
 } PauseKeyState;
 
+typedef struct _DVCSubscription
+{
+  gboolean notified;
+
+  GrdRdpDVCCreationStatusCallback callback;
+  gpointer user_data;
+} DVCSubscription;
+
+typedef struct _DVCNotification
+{
+  int32_t creation_status;
+  gboolean pending_status;
+
+  GHashTable *subscriptions;
+  uint32_t next_subscription_id;
+} DVCNotification;
+
 typedef struct _Pointer
 {
   uint8_t *bitmap;
@@ -134,6 +151,10 @@ struct _GrdSessionRdp
   GThread *graphics_thread;
   GMainContext *graphics_context;
 
+  GMutex dvc_notification_mutex;
+  GHashTable *dvc_table;
+  GSource *dvc_notification_source;
+
   GrdRdpSurface *rdp_surface;
   Pointer *last_pointer;
   GHashTable *pointer_cache;
@@ -650,6 +671,110 @@ grd_session_rdp_notify_error (GrdSessionRdp      *session_rdp,
   maybe_queue_close_session_idle (session_rdp);
 }
 
+static DVCNotification *
+dvc_notification_new (void)
+{
+  DVCNotification *dvc_notification;
+
+  dvc_notification = g_new0 (DVCNotification, 1);
+  dvc_notification->pending_status = TRUE;
+  dvc_notification->subscriptions = g_hash_table_new_full (NULL, NULL,
+                                                           NULL, g_free);
+
+  return dvc_notification;
+}
+
+static uint32_t
+get_next_free_dvc_subscription_id (DVCNotification *dvc_notification)
+{
+  uint32_t subscription_id = dvc_notification->next_subscription_id;
+
+  while (g_hash_table_contains (dvc_notification->subscriptions,
+                                GUINT_TO_POINTER (subscription_id)))
+    ++subscription_id;
+
+  dvc_notification->next_subscription_id = subscription_id + 1;
+
+  return subscription_id;
+}
+
+static uint32_t
+dvc_notification_add_subscription (DVCNotification *dvc_notification,
+                                   DVCSubscription *dvc_subscription)
+{
+  uint32_t subscription_id;
+
+  subscription_id = get_next_free_dvc_subscription_id (dvc_notification);
+  g_hash_table_insert (dvc_notification->subscriptions,
+                       GUINT_TO_POINTER (subscription_id), dvc_subscription);
+
+  return subscription_id;
+}
+
+uint32_t
+grd_session_rdp_subscribe_dvc_creation_status (GrdSessionRdp                   *session_rdp,
+                                               uint32_t                         channel_id,
+                                               GrdRdpDVCCreationStatusCallback  callback,
+                                               gpointer                         callback_user_data)
+{
+  DVCNotification *dvc_notification;
+  g_autofree DVCSubscription *dvc_subscription = NULL;
+  uint32_t subscription_id;
+  gboolean pending_notification = FALSE;
+
+  dvc_subscription = g_new0 (DVCSubscription, 1);
+  dvc_subscription->callback = callback;
+  dvc_subscription->user_data = callback_user_data;
+
+  g_mutex_lock (&session_rdp->dvc_notification_mutex);
+  if (g_hash_table_lookup_extended (session_rdp->dvc_table,
+                                    GUINT_TO_POINTER (channel_id),
+                                    NULL, (gpointer *) &dvc_notification))
+    {
+      subscription_id =
+        dvc_notification_add_subscription (dvc_notification,
+                                           g_steal_pointer (&dvc_subscription));
+
+      if (!dvc_notification->pending_status)
+        pending_notification = TRUE;
+    }
+  else
+    {
+      dvc_notification = dvc_notification_new ();
+
+      subscription_id =
+        dvc_notification_add_subscription (dvc_notification,
+                                           g_steal_pointer (&dvc_subscription));
+
+      g_hash_table_insert (session_rdp->dvc_table,
+                           GUINT_TO_POINTER (channel_id), dvc_notification);
+    }
+  g_mutex_unlock (&session_rdp->dvc_notification_mutex);
+
+  if (pending_notification)
+    g_source_set_ready_time (session_rdp->dvc_notification_source, 0);
+
+  return subscription_id;
+}
+
+void
+grd_session_rdp_unsubscribe_dvc_creation_status (GrdSessionRdp *session_rdp,
+                                                 uint32_t       channel_id,
+                                                 uint32_t       subscription_id)
+{
+  DVCNotification *dvc_notification;
+
+  g_mutex_lock (&session_rdp->dvc_notification_mutex);
+  if (!g_hash_table_lookup_extended (session_rdp->dvc_table,
+                                     GUINT_TO_POINTER (channel_id),
+                                     NULL, (gpointer *) &dvc_notification))
+    g_assert_not_reached ();
+
+  g_hash_table_remove (dvc_notification->subscriptions,
+                       GUINT_TO_POINTER (subscription_id));
+  g_mutex_unlock (&session_rdp->dvc_notification_mutex);
+}
+
 void
 grd_session_rdp_tear_down_channel (GrdSessionRdp *session_rdp,
                                    GrdRdpChannel  channel)
@@ -1848,6 +1973,56 @@ rdp_peer_context_free (freerdp_peer   *peer,
   g_mutex_clear (&rdp_peer_context->channel_mutex);
 }
 
+static BOOL
+dvc_creation_status (void     *user_data,
+                     uint32_t  channel_id,
+                     int32_t   creation_status)
+{
+  RdpPeerContext *rdp_peer_context = user_data;
+  GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp;
+  DVCNotification *dvc_notification;
+  gboolean pending_notification = FALSE;
+
+  g_debug ("[RDP.DRDYNVC] DVC channel id %u creation status: %i",
+           channel_id, creation_status);
+
+  g_mutex_lock (&session_rdp->dvc_notification_mutex);
+  if (g_hash_table_lookup_extended (session_rdp->dvc_table,
+                                    GUINT_TO_POINTER (channel_id),
+                                    NULL, (gpointer *) &dvc_notification))
+    {
+      if (dvc_notification->pending_status)
+        {
+          dvc_notification->creation_status = creation_status;
+          dvc_notification->pending_status = FALSE;
+
+          if (g_hash_table_size (dvc_notification->subscriptions) > 0)
+            pending_notification = TRUE;
+        }
+      else
+        {
+          g_warning ("[RDP.DRDYNVC] Status of channel %u already known. "
+                     "Discarding result", channel_id);
+        }
+    }
+  else
+    {
+      dvc_notification = dvc_notification_new ();
+
+      dvc_notification->creation_status = creation_status;
+      dvc_notification->pending_status = FALSE;
+
+      g_hash_table_insert (session_rdp->dvc_table,
+                           GUINT_TO_POINTER (channel_id), dvc_notification);
+    }
+  g_mutex_unlock (&session_rdp->dvc_notification_mutex);
+
+  if (pending_notification)
+    g_source_set_ready_time (session_rdp->dvc_notification_source, 0);
+
+  return TRUE;
+}
+
 static BOOL
 rdp_peer_context_new (freerdp_peer   *peer,
                       RdpPeerContext *rdp_peer_context)
@@ -1889,6 +2064,10 @@ rdp_peer_context_new (freerdp_peer   *peer,
       return FALSE;
     }
 
+  WTSVirtualChannelManagerSetDVCCreationCallback (rdp_peer_context->vcm,
+                                                  dvc_creation_status,
+                                                  rdp_peer_context);
+
   return TRUE;
 }
 
@@ -2207,6 +2386,11 @@ clear_session_sources (GrdSessionRdp *session_rdp)
       g_source_destroy (session_rdp->pending_encode_source);
       g_clear_pointer (&session_rdp->pending_encode_source, g_source_unref);
     }
+  if (session_rdp->dvc_notification_source)
+    {
+      g_source_destroy (session_rdp->dvc_notification_source);
+      g_clear_pointer (&session_rdp->dvc_notification_source, g_source_unref);
+    }
 }
 
 static gboolean
@@ -2451,6 +2635,7 @@ grd_session_rdp_dispose (GObject *object)
   g_clear_pointer (&session_rdp->pressed_unicode_keys, g_hash_table_unref);
   g_clear_pointer (&session_rdp->pressed_keys, g_hash_table_unref);
   g_clear_pointer (&session_rdp->pointer_cache, g_hash_table_unref);
+  g_clear_pointer (&session_rdp->dvc_table, g_hash_table_unref);
 
   g_clear_pointer (&session_rdp->stop_event, CloseHandle);
 
@@ -2464,6 +2649,7 @@ grd_session_rdp_finalize (GObject *object)
 
   g_mutex_clear (&session_rdp->monitor_config_mutex);
   g_mutex_clear (&session_rdp->close_session_mutex);
+  g_mutex_clear (&session_rdp->dvc_notification_mutex);
   g_mutex_clear (&session_rdp->rdp_flags_mutex);
   g_mutex_clear (&session_rdp->pending_jobs_mutex);
   g_cond_clear (&session_rdp->pending_jobs_cond);
@@ -2471,6 +2657,16 @@ grd_session_rdp_finalize (GObject *object)
   G_OBJECT_CLASS (grd_session_rdp_parent_class)->finalize (object);
 }
 
+static void
+dvc_notification_free (gpointer data)
+{
+  DVCNotification *dvc_notification = data;
+
+  g_clear_pointer (&dvc_notification->subscriptions, g_hash_table_unref);
+
+  g_free (dvc_notification);
+}
+
 static gboolean
 are_pointer_bitmaps_equal (gconstpointer a,
                            gconstpointer b)
@@ -2497,6 +2693,40 @@ are_pointer_bitmaps_equal (gconstpointer a,
   return TRUE;
 }
 
+static gboolean
+notify_channels (gpointer user_data)
+{
+  GrdSessionRdp *session_rdp = user_data;
+  GHashTableIter iter;
+  DVCNotification *dvc_notification;
+
+  g_mutex_lock (&session_rdp->dvc_notification_mutex);
+  g_hash_table_iter_init (&iter, session_rdp->dvc_table);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &dvc_notification))
+    {
+      GHashTableIter iter2;
+      DVCSubscription *dvc_subscription;
+
+      if (dvc_notification->pending_status)
+        continue;
+
+      g_hash_table_iter_init (&iter2, dvc_notification->subscriptions);
+      while (g_hash_table_iter_next (&iter2, NULL, (gpointer *) &dvc_subscription))
+        {
+          if (dvc_subscription->notified)
+            continue;
+
+          dvc_subscription->callback (dvc_subscription->user_data,
+                                      dvc_notification->creation_status);
+
+          dvc_subscription->notified = TRUE;
+        }
+    }
+  g_mutex_unlock (&session_rdp->dvc_notification_mutex);
+
+  return G_SOURCE_CONTINUE;
+}
+
 static gboolean
 encode_pending_frames (gpointer user_data)
 {
@@ -2550,6 +2780,8 @@ grd_session_rdp_init (GrdSessionRdp *session_rdp)
 {
   session_rdp->stop_event = CreateEvent (NULL, TRUE, FALSE, NULL);
 
+  session_rdp->dvc_table = g_hash_table_new_full (NULL, NULL,
+                                                  NULL, dvc_notification_free);
   session_rdp->pointer_cache = g_hash_table_new (NULL, are_pointer_bitmaps_equal);
   session_rdp->pressed_keys = g_hash_table_new (NULL, NULL);
   session_rdp->pressed_unicode_keys = g_hash_table_new (NULL, NULL);
@@ -2557,6 +2789,7 @@ grd_session_rdp_init (GrdSessionRdp *session_rdp)
   g_cond_init (&session_rdp->pending_jobs_cond);
   g_mutex_init (&session_rdp->pending_jobs_mutex);
   g_mutex_init (&session_rdp->rdp_flags_mutex);
+  g_mutex_init (&session_rdp->dvc_notification_mutex);
   g_mutex_init (&session_rdp->close_session_mutex);
   g_mutex_init (&session_rdp->monitor_config_mutex);
 
@@ -2564,6 +2797,13 @@ grd_session_rdp_init (GrdSessionRdp *session_rdp)
 
   session_rdp->graphics_context = g_main_context_new ();
 
+  session_rdp->dvc_notification_source = g_source_new (&session_source_funcs,
+                                                       sizeof (GSource));
+  g_source_set_callback (session_rdp->dvc_notification_source,
+                         notify_channels, session_rdp, NULL);
+  g_source_set_ready_time (session_rdp->dvc_notification_source, -1);
+  g_source_attach (session_rdp->dvc_notification_source, NULL);
+
   session_rdp->pending_encode_source = g_source_new (&session_source_funcs,
                                                      sizeof (GSource));
   g_source_set_callback (session_rdp->pending_encode_source,
diff --git a/src/grd-session-rdp.h b/src/grd-session-rdp.h
index 9ff709f0..6c750ead 100644
--- a/src/grd-session-rdp.h
+++ b/src/grd-session-rdp.h
@@ -45,6 +45,9 @@ typedef enum _GrdRdpChannel
   GRD_RDP_CHANNEL_NONE,
 } GrdRdpChannel;
 
+typedef void (* GrdRdpDVCCreationStatusCallback) (gpointer user_data,
+                                                  int32_t  creation_status);
+
 GrdSessionRdp *grd_session_rdp_new (GrdRdpServer      *rdp_server,
                                     GSocketConnection *connection,
                                     GrdHwAccelNvidia  *hwaccel_nvidia);
@@ -52,6 +55,15 @@ GrdSessionRdp *grd_session_rdp_new (GrdRdpServer      *rdp_server,
 void grd_session_rdp_notify_error (GrdSessionRdp      *session_rdp,
                                    GrdSessionRdpError  error_info);
 
+uint32_t grd_session_rdp_subscribe_dvc_creation_status (GrdSessionRdp                   *session_rdp,
+                                                        uint32_t                         channel_id,
+                                                        GrdRdpDVCCreationStatusCallback  callback,
+                                                        gpointer                         callback_user_data);
+
+void grd_session_rdp_unsubscribe_dvc_creation_status (GrdSessionRdp *session_rdp,
+                                                      uint32_t       channel_id,
+                                                      uint32_t       subscription_id);
+
 void grd_session_rdp_tear_down_channel (GrdSessionRdp *session_rdp,
                                         GrdRdpChannel  channel);
 


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