From 0c48096147ae1f17609fffbedd7f4787cde86ebc Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 14 Aug 2016 17:19:09 -0700 Subject: [PATCH] GUACAMOLE-51: Track and synchronize remote lock key state. --- src/protocols/rdp/keyboard.c | 111 +++++++++++++++++++++++++++++++++ src/protocols/rdp/keyboard.h | 33 ++++++++++ src/protocols/rdp/rdp_keymap.h | 16 +++++ 3 files changed, 160 insertions(+) diff --git a/src/protocols/rdp/keyboard.c b/src/protocols/rdp/keyboard.c index d7d2d518..d7e9a37c 100644 --- a/src/protocols/rdp/keyboard.c +++ b/src/protocols/rdp/keyboard.c @@ -31,6 +31,46 @@ #include #include +/** + * Translates the given keysym into the corresponding lock flag, as would be + * required by the RDP synchronize event. If the given keysym does not + * represent a lock key, zero is returned. + * + * @param keysym + * The keysym to translate into a RDP lock flag. + * + * @return + * The RDP lock flag which corresponds to the given keysym, or zero if the + * given keysym does not represent a lock key. + */ +static int guac_rdp_keyboard_lock_flag(int keysym) { + + /* Translate keysym into corresponding lock flag */ + switch (keysym) { + + /* Scroll lock */ + case 0xFF14: + return KBD_SYNC_SCROLL_LOCK; + + /* Kana lock */ + case 0xFF2D: + return KBD_SYNC_KANA_LOCK; + + /* Num lock */ + case 0xFF7F: + return KBD_SYNC_NUM_LOCK; + + /* Caps lock */ + case 0xFFE5: + return KBD_SYNC_CAPS_LOCK; + + } + + /* Not a lock key */ + return 0; + +} + /** * Immediately sends an RDP key event having the given scancode and flags. * @@ -113,6 +153,39 @@ static void guac_rdp_send_unicode_event(guac_rdp_client* rdp_client, } +/** + * Immediately sends an RDP synchonize event having the given flags. An RDP + * synchronize event sets the state of remote lock keys absolutely, where a + * lock key will be active only if its corresponding flag is set in the event. + * + * @param rdp_client + * The RDP client instance associated with the RDP session along which the + * synchronize event should be sent. + * + * @param flags + * Bitwise OR of the flags representing the lock keys which should be set, + * if any, as dictated by the RDP protocol. If no flags are set, then no + * lock keys will be active. + */ +static void guac_rdp_send_synchronize_event(guac_rdp_client* rdp_client, + int flags) { + + pthread_mutex_lock(&(rdp_client->rdp_lock)); + + /* Skip if not yet connected */ + freerdp* rdp_inst = rdp_client->rdp_inst; + if (rdp_inst == NULL) { + pthread_mutex_unlock(&(rdp_client->rdp_lock)); + return; + } + + /* Synchronize lock key states */ + rdp_inst->input->SynchronizeEvent(rdp_inst->input, flags); + + pthread_mutex_unlock(&(rdp_client->rdp_lock)); + +} + /** * Loads all keysym/scancode mappings declared within the given keymap and its * parent keymap, if any. These mappings are stored within the given @@ -187,6 +260,11 @@ int guac_rdp_keyboard_send_event(guac_rdp_keyboard* keyboard, /* If defined, send event */ if (keysym_desc->scancode != 0) { + /* Update remote lock state as necessary */ + guac_rdp_keyboard_update_locks(keyboard, + keysym_desc->set_locks, + keysym_desc->clear_locks); + /* If defined, send any prerequesite keys that must be set */ if (keysym_desc->set_keysyms != NULL) guac_rdp_keyboard_send_events(keyboard, @@ -270,9 +348,42 @@ void guac_rdp_keyboard_send_events(guac_rdp_keyboard* keyboard, } +void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard, + int set_flags, int clear_flags) { + + guac_client* client = keyboard->client; + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + + /* Calculate updated lock flags */ + int lock_flags = (keyboard->lock_flags | set_flags) & ~clear_flags; + + /* Synchronize remote side only if lock flags have changed */ + if (lock_flags != keyboard->lock_flags) { + guac_rdp_send_synchronize_event(rdp_client, lock_flags); + keyboard->lock_flags = lock_flags; + } + +} + int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, int keysym, int pressed) { + /* Synchronize lock keys states, if this has not yet been done */ + if (!keyboard->synchronized) { + + guac_client* client = keyboard->client; + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + + /* Synchronize remote lock key states with local state */ + guac_rdp_send_synchronize_event(rdp_client, keyboard->lock_flags); + keyboard->synchronized = 1; + + } + + /* Toggle lock flag, if any */ + if (pressed) + keyboard->lock_flags ^= guac_rdp_keyboard_lock_flag(keysym); + /* Update keysym state */ if (GUAC_RDP_KEYSYM_STORABLE(keysym)) GUAC_RDP_KEYSYM_LOOKUP(keyboard->keysym_state, keysym) = pressed; diff --git a/src/protocols/rdp/keyboard.h b/src/protocols/rdp/keyboard.h index aebc58e7..8282c18c 100644 --- a/src/protocols/rdp/keyboard.h +++ b/src/protocols/rdp/keyboard.h @@ -35,6 +35,19 @@ typedef struct guac_rdp_keyboard { */ guac_client* client; + /** + * The local state of all known lock keys, as a bitwise OR of all RDP lock + * key flags. Legal flags are KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, + * KBD_SYNC_CAPS_LOCK, and KBD_SYNC_KANA_LOCK. + */ + int lock_flags; + + /** + * Whether the states of remote lock keys (Caps lock, Num lock, etc.) have + * been synchronized with local lock key states. + */ + int synchronized; + /** * The keymap to use when translating keysyms into scancodes or sequences * of scancodes for RDP. @@ -127,6 +140,26 @@ int guac_rdp_keyboard_send_event(guac_rdp_keyboard* keyboard, void guac_rdp_keyboard_send_events(guac_rdp_keyboard* keyboard, const int* keysym_string, int from, int to); +/** + * Updates the local state of the lock keys (such as Caps lock or Num lock), + * synchronizing the remote state of those keys if it is expected to differ. + * + * @param keyboard + * The guac_rdp_keyboard associated with the current RDP session. + * + * @param set_flags + * The lock key flags which should be set. Legal flags are + * KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and + * KBD_SYNC_KANA_LOCK. + * + * @param clear_flags + * The lock key flags which should be cleared. Legal flags are + * KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and + * KBD_SYNC_KANA_LOCK. + */ +void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard, + int set_flags, int clear_flags); + /** * Updates the local state of the given keysym, sending the key events required * to replicate that state remotely (on the RDP server). The key events sent diff --git a/src/protocols/rdp/rdp_keymap.h b/src/protocols/rdp/rdp_keymap.h index 01e11efe..b299bdc1 100644 --- a/src/protocols/rdp/rdp_keymap.h +++ b/src/protocols/rdp/rdp_keymap.h @@ -62,6 +62,22 @@ typedef struct guac_rdp_keysym_desc { */ const int* clear_keysyms; + /** + * Bitwise OR of the flags of all lock keys (ie: Caps lock, Num lock, etc.) + * which must be active for this keysym to be properly typed. Legal flags + * are KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and + * KBD_SYNC_KANA_LOCK. + */ + int set_locks; + + /** + * Bitwise OR of the flags of all lock keys (ie: Caps lock, Num lock, etc.) + * which must be inactive for this keysym to be properly typed. Legal flags + * are KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and + * KBD_SYNC_KANA_LOCK. + */ + int clear_locks; + } guac_rdp_keysym_desc; /**