mirror of
https://github.com/morgan9e/grd
synced 2026-04-13 16:04:13 +09:00
486 lines
15 KiB
C
486 lines
15 KiB
C
/*
|
|
* 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;
|
|
}
|