mirror of
https://github.com/morgan9e/grd
synced 2026-04-14 00:14:18 +09:00
.
This commit is contained in:
485
grd-rdp-event-queue.c
Normal file
485
grd-rdp-event-queue.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user