This commit is contained in:
2026-02-13 13:06:50 +09:00
commit b54066842b
249 changed files with 69547 additions and 0 deletions

485
grd-rdp-event-queue.c Normal file
View File

@@ -0,0 +1,485 @@
/*
* Copyright (C) 2021 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-event-queue.h"
#include <gio/gio.h>
#include <xkbcommon/xkbcommon-keysyms.h>
typedef enum _RdpEventType
{
RDP_EVENT_TYPE_NONE,
RDP_EVENT_TYPE_INPUT_SYNC,
RDP_EVENT_TYPE_INPUT_KBD_KEYCODE,
RDP_EVENT_TYPE_INPUT_KBD_KEYSYM,
RDP_EVENT_TYPE_INPUT_PTR_MOTION,
RDP_EVENT_TYPE_INPUT_PTR_MOTION_ABS,
RDP_EVENT_TYPE_INPUT_PTR_BUTTON,
RDP_EVENT_TYPE_INPUT_PTR_AXIS,
} RdpEventType;
typedef struct _RdpEvent
{
RdpEventType type;
/* RDP_EVENT_TYPE_INPUT_KBD_KEYCODE */
struct
{
uint32_t keycode;
GrdKeyState state;
} input_kbd_keycode;
/* RDP_EVENT_TYPE_INPUT_KBD_KEYSYM */
struct
{
uint32_t keysym;
GrdKeyState state;
} input_kbd_keysym;
/* RDP_EVENT_TYPE_INPUT_PTR_MOTION */
struct
{
double dx;
double dy;
} input_ptr_motion;
/* RDP_EVENT_TYPE_INPUT_PTR_MOTION_ABS */
struct
{
GrdStream *stream;
GrdEventMotionAbs motion_abs;
} input_ptr_motion_abs;
/* RDP_EVENT_TYPE_INPUT_PTR_BUTTON */
struct
{
int32_t button;
GrdButtonState state;
} input_ptr_button;
/* RDP_EVENT_TYPE_INPUT_PTR_AXIS */
struct
{
double dx;
double dy;
GrdPointerAxisFlags flags;
} input_ptr_axis;
/* RDP_EVENT_TYPE_INPUT_SYNC */
struct
{
gboolean caps_lock_state;
gboolean num_lock_state;
gboolean needs_sync_ping;
} input_sync;
} RdpEvent;
struct _GrdRdpEventQueue
{
GObject parent;
GrdSessionRdp *session_rdp;
GSource *flush_source;
GMutex event_mutex;
GQueue *queue;
GCancellable *pending_sync_cancellable;
gboolean expected_caps_lock_state;
gboolean expected_num_lock_state;
gboolean caps_lock_state;
gboolean num_lock_state;
};
G_DEFINE_TYPE (GrdRdpEventQueue, grd_rdp_event_queue, G_TYPE_OBJECT)
void
free_rdp_event (gpointer data)
{
RdpEvent *rdp_event = data;
g_clear_object (&rdp_event->input_ptr_motion_abs.stream);
g_free (rdp_event);
}
static void
flush_input_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
GrdSession *session = GRD_SESSION (source_object);
g_autoptr (GError) error = NULL;
GrdRdpEventQueue *rdp_event_queue;
if (!grd_session_flush_input_finish (session, result, &error))
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("Failed to flush input: %s", error->message);
return;
}
rdp_event_queue = GRD_RDP_EVENT_QUEUE (user_data);
g_clear_object (&rdp_event_queue->pending_sync_cancellable);
if (rdp_event_queue->expected_caps_lock_state != rdp_event_queue->caps_lock_state)
{
g_debug ("Synchronizing caps lock state to be %s, pressing caps lock key",
rdp_event_queue->expected_caps_lock_state ? "locked": "unlocked");
grd_session_notify_keyboard_keysym (session, XKB_KEY_Caps_Lock,
GRD_KEY_STATE_PRESSED);
grd_session_notify_keyboard_keysym (session, XKB_KEY_Caps_Lock,
GRD_KEY_STATE_RELEASED);
}
if (rdp_event_queue->expected_num_lock_state != rdp_event_queue->num_lock_state)
{
g_debug ("Synchronizing num lock state to be %s, pressing num lock key",
rdp_event_queue->expected_num_lock_state ? "locked": "unlocked");
grd_session_notify_keyboard_keysym (session, XKB_KEY_Num_Lock,
GRD_KEY_STATE_PRESSED);
grd_session_notify_keyboard_keysym (session, XKB_KEY_Num_Lock,
GRD_KEY_STATE_RELEASED);
}
}
static void
handle_synchronization_event (GrdRdpEventQueue *rdp_event_queue,
RdpEvent *rdp_event)
{
GrdSession *session = GRD_SESSION (rdp_event_queue->session_rdp);
g_cancellable_cancel (rdp_event_queue->pending_sync_cancellable);
g_clear_object (&rdp_event_queue->pending_sync_cancellable);
rdp_event_queue->expected_caps_lock_state = rdp_event->input_sync.caps_lock_state;
rdp_event_queue->expected_num_lock_state = rdp_event->input_sync.num_lock_state;
rdp_event_queue->pending_sync_cancellable = g_cancellable_new ();
if (!grd_session_is_ready (session))
return;
grd_rdp_event_queue_flush_synchronization (rdp_event_queue);
}
void
grd_rdp_event_queue_flush_synchronization (GrdRdpEventQueue *rdp_event_queue)
{
g_assert (grd_session_is_ready (GRD_SESSION (rdp_event_queue->session_rdp)));
if (!rdp_event_queue->pending_sync_cancellable)
return;
grd_session_flush_input_async (GRD_SESSION (rdp_event_queue->session_rdp),
rdp_event_queue->pending_sync_cancellable,
flush_input_cb,
rdp_event_queue);
}
static void
process_rdp_events (GrdRdpEventQueue *rdp_event_queue)
{
GrdSession *session = GRD_SESSION (rdp_event_queue->session_rdp);
RdpEvent *rdp_event;
while ((rdp_event = g_queue_pop_head (rdp_event_queue->queue)))
{
switch (rdp_event->type)
{
case RDP_EVENT_TYPE_NONE:
break;
case RDP_EVENT_TYPE_INPUT_SYNC:
handle_synchronization_event (rdp_event_queue, rdp_event);
break;
case RDP_EVENT_TYPE_INPUT_KBD_KEYCODE:
grd_session_notify_keyboard_keycode (
session, rdp_event->input_kbd_keycode.keycode,
rdp_event->input_kbd_keycode.state);
break;
case RDP_EVENT_TYPE_INPUT_KBD_KEYSYM:
grd_session_notify_keyboard_keysym (session,
rdp_event->input_kbd_keysym.keysym,
rdp_event->input_kbd_keysym.state);
break;
case RDP_EVENT_TYPE_INPUT_PTR_MOTION:
grd_session_notify_pointer_motion (session,
rdp_event->input_ptr_motion.dx,
rdp_event->input_ptr_motion.dy);
break;
case RDP_EVENT_TYPE_INPUT_PTR_MOTION_ABS:
grd_session_notify_pointer_motion_absolute (
session, rdp_event->input_ptr_motion_abs.stream,
&rdp_event->input_ptr_motion_abs.motion_abs);
break;
case RDP_EVENT_TYPE_INPUT_PTR_BUTTON:
grd_session_notify_pointer_button (session,
rdp_event->input_ptr_button.button,
rdp_event->input_ptr_button.state);
break;
case RDP_EVENT_TYPE_INPUT_PTR_AXIS:
grd_session_notify_pointer_axis (session,
rdp_event->input_ptr_axis.dx,
rdp_event->input_ptr_axis.dy,
rdp_event->input_ptr_axis.flags);
break;
}
free_rdp_event (rdp_event);
}
}
void
grd_rdp_event_queue_flush (GrdRdpEventQueue *rdp_event_queue)
{
g_mutex_lock (&rdp_event_queue->event_mutex);
process_rdp_events (rdp_event_queue);
g_mutex_unlock (&rdp_event_queue->event_mutex);
}
static void
queue_rdp_event (GrdRdpEventQueue *rdp_event_queue,
RdpEvent *rdp_event)
{
g_mutex_lock (&rdp_event_queue->event_mutex);
g_queue_push_tail (rdp_event_queue->queue, rdp_event);
g_mutex_unlock (&rdp_event_queue->event_mutex);
g_source_set_ready_time (rdp_event_queue->flush_source, 0);
}
void
grd_rdp_event_queue_add_input_event_keyboard_keycode (GrdRdpEventQueue *rdp_event_queue,
uint32_t keycode,
GrdKeyState state)
{
RdpEvent *rdp_event;
rdp_event = g_malloc0 (sizeof (RdpEvent));
rdp_event->type = RDP_EVENT_TYPE_INPUT_KBD_KEYCODE;
rdp_event->input_kbd_keycode.keycode = keycode;
rdp_event->input_kbd_keycode.state = state;
queue_rdp_event (rdp_event_queue, rdp_event);
}
void
grd_rdp_event_queue_add_input_event_keyboard_keysym (GrdRdpEventQueue *rdp_event_queue,
uint32_t keysym,
GrdKeyState state)
{
RdpEvent *rdp_event;
rdp_event = g_malloc0 (sizeof (RdpEvent));
rdp_event->type = RDP_EVENT_TYPE_INPUT_KBD_KEYSYM;
rdp_event->input_kbd_keysym.keysym = keysym;
rdp_event->input_kbd_keysym.state = state;
queue_rdp_event (rdp_event_queue, rdp_event);
}
void
grd_rdp_event_queue_add_input_event_pointer_motion (GrdRdpEventQueue *rdp_event_queue,
double dx,
double dy)
{
RdpEvent *rdp_event;
rdp_event = g_new0 (RdpEvent, 1);
rdp_event->type = RDP_EVENT_TYPE_INPUT_PTR_MOTION;
rdp_event->input_ptr_motion.dx = dx;
rdp_event->input_ptr_motion.dy = dy;
queue_rdp_event (rdp_event_queue, rdp_event);
}
void
grd_rdp_event_queue_add_input_event_pointer_motion_abs (GrdRdpEventQueue *rdp_event_queue,
GrdStream *stream,
const GrdEventMotionAbs *motion_abs)
{
RdpEvent *rdp_event;
rdp_event = g_malloc0 (sizeof (RdpEvent));
rdp_event->type = RDP_EVENT_TYPE_INPUT_PTR_MOTION_ABS;
rdp_event->input_ptr_motion_abs.stream = stream;
rdp_event->input_ptr_motion_abs.motion_abs = *motion_abs;
queue_rdp_event (rdp_event_queue, rdp_event);
}
void
grd_rdp_event_queue_add_input_event_pointer_button (GrdRdpEventQueue *rdp_event_queue,
int32_t button,
GrdButtonState state)
{
RdpEvent *rdp_event;
rdp_event = g_malloc0 (sizeof (RdpEvent));
rdp_event->type = RDP_EVENT_TYPE_INPUT_PTR_BUTTON;
rdp_event->input_ptr_button.button = button;
rdp_event->input_ptr_button.state = state;
queue_rdp_event (rdp_event_queue, rdp_event);
}
void
grd_rdp_event_queue_add_input_event_pointer_axis (GrdRdpEventQueue *rdp_event_queue,
double dx,
double dy,
GrdPointerAxisFlags flags)
{
RdpEvent *rdp_event;
rdp_event = g_malloc0 (sizeof (RdpEvent));
rdp_event->type = RDP_EVENT_TYPE_INPUT_PTR_AXIS;
rdp_event->input_ptr_axis.dx = dx;
rdp_event->input_ptr_axis.dy = dy;
rdp_event->input_ptr_axis.flags = flags;
queue_rdp_event (rdp_event_queue, rdp_event);
}
void
grd_rdp_event_queue_update_caps_lock_state (GrdRdpEventQueue *rdp_event_queue,
gboolean caps_lock_state)
{
g_debug ("Updated current caps lock state to %s",
caps_lock_state ? "locked" : "unlocked");
rdp_event_queue->caps_lock_state = caps_lock_state;
}
void
grd_rdp_event_queue_update_num_lock_state (GrdRdpEventQueue *rdp_event_queue,
gboolean num_lock_state)
{
g_debug ("Updated current num lock state to %s",
num_lock_state ? "locked" : "unlocked");
rdp_event_queue->num_lock_state = num_lock_state;
}
void
grd_rdp_event_queue_add_synchronization_event (GrdRdpEventQueue *rdp_event_queue,
gboolean caps_lock_state,
gboolean num_lock_state)
{
RdpEvent *rdp_event;
rdp_event = g_new0 (RdpEvent, 1);
rdp_event->type = RDP_EVENT_TYPE_INPUT_SYNC;
rdp_event->input_sync.caps_lock_state = caps_lock_state;
rdp_event->input_sync.num_lock_state = num_lock_state;
rdp_event->input_sync.needs_sync_ping = TRUE;
queue_rdp_event (rdp_event_queue, rdp_event);
}
static gboolean
flush_rdp_events (gpointer user_data)
{
GrdRdpEventQueue *rdp_event_queue = user_data;
g_mutex_lock (&rdp_event_queue->event_mutex);
process_rdp_events (rdp_event_queue);
g_mutex_unlock (&rdp_event_queue->event_mutex);
return G_SOURCE_CONTINUE;
}
static gboolean
flush_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
g_source_set_ready_time (source, -1);
return callback (user_data);
}
static GSourceFuncs flush_source_funcs =
{
.dispatch = flush_source_dispatch,
};
GrdRdpEventQueue *
grd_rdp_event_queue_new (GrdSessionRdp *session_rdp)
{
GrdRdpEventQueue *rdp_event_queue;
GSource *flush_source;
rdp_event_queue = g_object_new (GRD_TYPE_RDP_EVENT_QUEUE, NULL);
rdp_event_queue->session_rdp = session_rdp;
flush_source = g_source_new (&flush_source_funcs, sizeof (GSource));
g_source_set_callback (flush_source, flush_rdp_events, rdp_event_queue, NULL);
g_source_set_ready_time (flush_source, -1);
g_source_attach (flush_source, NULL);
rdp_event_queue->flush_source = flush_source;
return rdp_event_queue;
}
static void
grd_rdp_event_queue_dispose (GObject *object)
{
GrdRdpEventQueue *rdp_event_queue = GRD_RDP_EVENT_QUEUE (object);
if (rdp_event_queue->flush_source)
{
g_source_destroy (rdp_event_queue->flush_source);
g_clear_pointer (&rdp_event_queue->flush_source, g_source_unref);
}
G_OBJECT_CLASS (grd_rdp_event_queue_parent_class)->dispose (object);
}
static void
grd_rdp_event_queue_finalize (GObject *object)
{
GrdRdpEventQueue *rdp_event_queue = GRD_RDP_EVENT_QUEUE (object);
g_cancellable_cancel (rdp_event_queue->pending_sync_cancellable);
g_clear_object (&rdp_event_queue->pending_sync_cancellable);
g_mutex_clear (&rdp_event_queue->event_mutex);
g_queue_free_full (rdp_event_queue->queue, free_rdp_event);
G_OBJECT_CLASS (grd_rdp_event_queue_parent_class)->finalize (object);
}
static void
grd_rdp_event_queue_init (GrdRdpEventQueue *rdp_event_queue)
{
rdp_event_queue->queue = g_queue_new ();
g_mutex_init (&rdp_event_queue->event_mutex);
}
static void
grd_rdp_event_queue_class_init (GrdRdpEventQueueClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = grd_rdp_event_queue_dispose;
object_class->finalize = grd_rdp_event_queue_finalize;
}