/* * 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 #include 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; }