mirror of
https://github.com/morgan9e/grd
synced 2026-04-14 00:14:18 +09:00
.
This commit is contained in:
328
grd-rdp-dvc-handler.c
Normal file
328
grd-rdp-dvc-handler.c
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Pascal Nowack
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "grd-rdp-dvc-handler.h"
|
||||
|
||||
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;
|
||||
|
||||
struct _GrdRdpDvcHandler
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
GMutex dvc_notification_mutex;
|
||||
GHashTable *dvc_table;
|
||||
GSource *dvc_notification_source;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GrdRdpDvcHandler, grd_rdp_dvc_handler,
|
||||
G_TYPE_OBJECT)
|
||||
|
||||
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_rdp_dvc_handler_subscribe_dvc_creation_status (GrdRdpDvcHandler *dvc_handler,
|
||||
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 (&dvc_handler->dvc_notification_mutex);
|
||||
if (g_hash_table_lookup_extended (dvc_handler->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 (dvc_handler->dvc_table,
|
||||
GUINT_TO_POINTER (channel_id), dvc_notification);
|
||||
}
|
||||
g_mutex_unlock (&dvc_handler->dvc_notification_mutex);
|
||||
|
||||
if (pending_notification)
|
||||
g_source_set_ready_time (dvc_handler->dvc_notification_source, 0);
|
||||
|
||||
return subscription_id;
|
||||
}
|
||||
|
||||
void
|
||||
grd_rdp_dvc_handler_unsubscribe_dvc_creation_status (GrdRdpDvcHandler *dvc_handler,
|
||||
uint32_t channel_id,
|
||||
uint32_t subscription_id)
|
||||
{
|
||||
DVCNotification *dvc_notification;
|
||||
|
||||
g_mutex_lock (&dvc_handler->dvc_notification_mutex);
|
||||
if (!g_hash_table_lookup_extended (dvc_handler->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 (&dvc_handler->dvc_notification_mutex);
|
||||
}
|
||||
|
||||
static BOOL
|
||||
dvc_creation_status (void *user_data,
|
||||
uint32_t channel_id,
|
||||
int32_t creation_status)
|
||||
{
|
||||
GrdRdpDvcHandler *dvc_handler = user_data;
|
||||
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 (&dvc_handler->dvc_notification_mutex);
|
||||
if (g_hash_table_lookup_extended (dvc_handler->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 (dvc_handler->dvc_table,
|
||||
GUINT_TO_POINTER (channel_id), dvc_notification);
|
||||
}
|
||||
g_mutex_unlock (&dvc_handler->dvc_notification_mutex);
|
||||
|
||||
if (pending_notification)
|
||||
g_source_set_ready_time (dvc_handler->dvc_notification_source, 0);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GrdRdpDvcHandler *
|
||||
grd_rdp_dvc_handler_new (HANDLE vcm)
|
||||
{
|
||||
GrdRdpDvcHandler *dvc_handler;
|
||||
|
||||
dvc_handler = g_object_new (GRD_TYPE_RDP_DVC_HANDLER, NULL);
|
||||
|
||||
WTSVirtualChannelManagerSetDVCCreationCallback (vcm, dvc_creation_status,
|
||||
dvc_handler);
|
||||
|
||||
return dvc_handler;
|
||||
}
|
||||
|
||||
static void
|
||||
grd_rdp_dvc_handler_dispose (GObject *object)
|
||||
{
|
||||
GrdRdpDvcHandler *dvc_handler = GRD_RDP_DVC_HANDLER (object);
|
||||
|
||||
if (dvc_handler->dvc_notification_source)
|
||||
{
|
||||
g_source_destroy (dvc_handler->dvc_notification_source);
|
||||
g_clear_pointer (&dvc_handler->dvc_notification_source, g_source_unref);
|
||||
}
|
||||
|
||||
g_clear_pointer (&dvc_handler->dvc_table, g_hash_table_unref);
|
||||
|
||||
G_OBJECT_CLASS (grd_rdp_dvc_handler_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
grd_rdp_dvc_handler_finalize (GObject *object)
|
||||
{
|
||||
GrdRdpDvcHandler *dvc_handler = GRD_RDP_DVC_HANDLER (object);
|
||||
|
||||
g_mutex_clear (&dvc_handler->dvc_notification_mutex);
|
||||
|
||||
G_OBJECT_CLASS (grd_rdp_dvc_handler_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
|
||||
notify_channels (gpointer user_data)
|
||||
{
|
||||
GrdRdpDvcHandler *dvc_handler = user_data;
|
||||
GHashTableIter iter;
|
||||
DVCNotification *dvc_notification;
|
||||
|
||||
g_mutex_lock (&dvc_handler->dvc_notification_mutex);
|
||||
g_hash_table_iter_init (&iter, dvc_handler->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 (&dvc_handler->dvc_notification_mutex);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
source_dispatch (GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_source_set_ready_time (source, -1);
|
||||
|
||||
return callback (user_data);
|
||||
}
|
||||
|
||||
static GSourceFuncs source_funcs =
|
||||
{
|
||||
.dispatch = source_dispatch,
|
||||
};
|
||||
|
||||
static void
|
||||
grd_rdp_dvc_handler_init (GrdRdpDvcHandler *dvc_handler)
|
||||
{
|
||||
dvc_handler->dvc_table =
|
||||
g_hash_table_new_full (NULL, NULL,
|
||||
NULL, dvc_notification_free);
|
||||
|
||||
g_mutex_init (&dvc_handler->dvc_notification_mutex);
|
||||
|
||||
dvc_handler->dvc_notification_source = g_source_new (&source_funcs,
|
||||
sizeof (GSource));
|
||||
g_source_set_callback (dvc_handler->dvc_notification_source,
|
||||
notify_channels, dvc_handler, NULL);
|
||||
g_source_set_ready_time (dvc_handler->dvc_notification_source, -1);
|
||||
g_source_attach (dvc_handler->dvc_notification_source, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
grd_rdp_dvc_handler_class_init (GrdRdpDvcHandlerClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = grd_rdp_dvc_handler_dispose;
|
||||
object_class->finalize = grd_rdp_dvc_handler_finalize;
|
||||
}
|
||||
Reference in New Issue
Block a user