From 337f3bbff280844012eeedb16311649ae55565d8 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 23 Jun 2020 21:46:26 -0700 Subject: [PATCH 1/5] GUACAMOLE-518: Map both Right Alt and AltGr to Windows' Right Alt. Windows expects the Right Alt key to be sent for AltGr. --- src/protocols/rdp/keyboard.c | 1 + src/protocols/rdp/keymaps/base.keymap | 1 + 2 files changed, 2 insertions(+) diff --git a/src/protocols/rdp/keyboard.c b/src/protocols/rdp/keyboard.c index a75b7340..9a5d2a51 100644 --- a/src/protocols/rdp/keyboard.c +++ b/src/protocols/rdp/keyboard.c @@ -619,6 +619,7 @@ void guac_rdp_keyboard_update_modifiers(guac_rdp_keyboard* keyboard, guac_rdp_keyboard_update_keysym(keyboard, 0xFE03, 1, GUAC_RDP_KEY_SOURCE_SYNTHETIC); } else if (clear_flags & GUAC_RDP_KEYMAP_MODIFIER_ALTGR) { + guac_rdp_keyboard_update_keysym(keyboard, 0xFFEA, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); guac_rdp_keyboard_update_keysym(keyboard, 0xFE03, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); } diff --git a/src/protocols/rdp/keymaps/base.keymap b/src/protocols/rdp/keymaps/base.keymap index d02c5eab..09b664c6 100644 --- a/src/protocols/rdp/keymaps/base.keymap +++ b/src/protocols/rdp/keymaps/base.keymap @@ -84,6 +84,7 @@ map 0x1D ~ 0xffe3 # Control_L map +ext 0x1D ~ 0xffe4 # Control_R map 0x38 ~ 0xffe9 # Alt_L map +ext 0x38 ~ 0xffea # Alt_R +map +ext 0x38 ~ 0xfe03 # AltGr map +ext 0x5B ~ 0xffeb # Super_L map +ext 0x5C ~ 0xffec # Super_R map +ext 0x5D ~ 0xff67 # Menu From 7d17e6898aba5d8432fdaef1e82e0eb7e094778a Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 23 Jun 2020 21:47:48 -0700 Subject: [PATCH 2/5] GUACAMOLE-518: Remove unused GUAC_KEYSYMS_* constants. --- src/protocols/rdp/keymap.c | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/protocols/rdp/keymap.c b/src/protocols/rdp/keymap.c index b71b402d..efb63d77 100644 --- a/src/protocols/rdp/keymap.c +++ b/src/protocols/rdp/keymap.c @@ -21,28 +21,6 @@ #include -const int GUAC_KEYSYMS_SHIFT[] = {0xFFE1, 0}; -const int GUAC_KEYSYMS_ALL_SHIFT[] = {0xFFE1, 0xFFE2, 0}; - -const int GUAC_KEYSYMS_ALTGR[] = {0xFFEA, 0}; -const int GUAC_KEYSYMS_SHIFT_ALTGR[] = {0xFFE1, 0xFFEA, 0}; -const int GUAC_KEYSYMS_ALL_SHIFT_ALTGR[] = {0xFFE1, 0xFFE2, 0xFFEA, 0}; - -const int GUAC_KEYSYMS_CTRL[] = {0xFFE3, 0}; -const int GUAC_KEYSYMS_ALL_CTRL[] = {0xFFE3, 0xFFE4, 0}; - -const int GUAC_KEYSYMS_ALT[] = {0xFFE9, 0}; -const int GUAC_KEYSYMS_ALL_ALT[] = {0xFFE9, 0xFFEA, 0}; - -const int GUAC_KEYSYMS_CTRL_ALT[] = {0xFFE3, 0xFFE9, 0}; - -const int GUAC_KEYSYMS_ALL_MODIFIERS[] = { - 0xFFE1, 0xFFE2, /* Left and right shift */ - 0xFFE3, 0xFFE4, /* Left and right control */ - 0xFFE9, 0xFFEA, /* Left and right alt (AltGr) */ - 0 -}; - const guac_rdp_keymap* guac_rdp_keymap_find(const char* name) { /* For each keymap */ From 2407157d00de32b62a2766942b172994075c740d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 23 Jun 2020 22:37:15 -0700 Subject: [PATCH 3/5] GUACAMOLE-518: Handle modifier status correctly when multiple modifier keys are involved. --- src/protocols/rdp/keyboard.c | 108 +++++++++++++++++------------------ src/protocols/rdp/keyboard.h | 40 ++++++++++--- src/protocols/rdp/keymap.h | 55 ++++++++++++++++++ 3 files changed, 139 insertions(+), 64 deletions(-) diff --git a/src/protocols/rdp/keyboard.c b/src/protocols/rdp/keyboard.c index 9a5d2a51..0f1a49ba 100644 --- a/src/protocols/rdp/keyboard.c +++ b/src/protocols/rdp/keyboard.c @@ -28,39 +28,6 @@ #include -/** - * Translates the given keysym into the corresponding modifier flag, as defined - * by keymap.h. If the given keysym does not represent a modifier key, zero is - * returned. - * - * @param keysym - * The keysym to translate into a modifier flag. - * - * @return - * The modifier flag which corresponds to the given keysym, or zero if the - * given keysym does not represent a modifier key. - */ -static int guac_rdp_keyboard_modifier_flag(int keysym) { - - /* Translate keysym into corresponding modifier flag */ - switch (keysym) { - - /* Shift */ - case 0xFFE1: - case 0xFFE2: - return GUAC_RDP_KEYMAP_MODIFIER_SHIFT; - - /* AltGr */ - case 0xFE03: - return GUAC_RDP_KEYMAP_MODIFIER_ALTGR; - - } - - /* Not a modifier */ - return 0; - -} - /** * Translates the given keysym into the corresponding lock flag, as would be * required by the RDP synchronize event. If the given keysym does not @@ -79,19 +46,19 @@ static int guac_rdp_keyboard_lock_flag(int keysym) { switch (keysym) { /* Scroll lock */ - case 0xFF14: + case GUAC_RDP_KEYSYM_SCROLL_LOCK: return KBD_SYNC_SCROLL_LOCK; /* Kana lock */ - case 0xFF2D: + case GUAC_RDP_KEYSYM_KANA_LOCK: return KBD_SYNC_KANA_LOCK; /* Num lock */ - case 0xFF7F: + case GUAC_RDP_KEYSYM_NUM_LOCK: return KBD_SYNC_NUM_LOCK; /* Caps lock */ - case 0xFFE5: + case GUAC_RDP_KEYSYM_CAPS_LOCK: return KBD_SYNC_CAPS_LOCK; } @@ -288,6 +255,8 @@ static int guac_rdp_count_bits(unsigned int value) { static int guac_rdp_keyboard_get_cost(guac_rdp_keyboard* keyboard, const guac_rdp_keysym_desc* def) { + unsigned int modifier_flags = guac_rdp_keyboard_get_modifier_flags(keyboard); + /* Each change to any key requires one event, by definition */ int cost = 1; @@ -296,7 +265,7 @@ static int guac_rdp_keyboard_get_cost(guac_rdp_keyboard* keyboard, cost += guac_rdp_count_bits(update_locks) * 2; /* Each change to a modifier requires one key event */ - unsigned int update_modifiers = (def->clear_modifiers & keyboard->modifier_flags) | (def->set_modifiers & ~keyboard->modifier_flags); + unsigned int update_modifiers = (def->clear_modifiers & modifier_flags) | (def->set_modifiers & ~modifier_flags); cost += guac_rdp_count_bits(update_modifiers); return cost; @@ -489,6 +458,37 @@ int guac_rdp_keyboard_is_defined(guac_rdp_keyboard* keyboard, int keysym) { } +int guac_rdp_keyboard_is_pressed(guac_rdp_keyboard* keyboard, int keysym) { + + guac_rdp_key* key = guac_rdp_keyboard_get_key(keyboard, keysym); + return key != NULL && key->pressed != NULL; + +} + +unsigned int guac_rdp_keyboard_get_modifier_flags(guac_rdp_keyboard* keyboard) { + + unsigned int modifier_flags = 0; + + /* Shift */ + if (guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LSHIFT) + || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RSHIFT)) + modifier_flags |= GUAC_RDP_KEYMAP_MODIFIER_SHIFT; + + /* Dedicated AltGr key */ + if (guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RALT) + || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_ALTGR)) + modifier_flags |= GUAC_RDP_KEYMAP_MODIFIER_ALTGR; + + /* AltGr via Ctrl+Alt */ + if (guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LALT) + && (guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RCTRL) + || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LCTRL))) + modifier_flags |= GUAC_RDP_KEYMAP_MODIFIER_ALTGR; + + return modifier_flags; + +} + /** * Presses/releases the requested key by sending one or more RDP key events, as * defined within the keymap defining that key. @@ -599,28 +599,33 @@ void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard, void guac_rdp_keyboard_update_modifiers(guac_rdp_keyboard* keyboard, unsigned int set_flags, unsigned int clear_flags) { + unsigned int modifier_flags = guac_rdp_keyboard_get_modifier_flags(keyboard); + /* Only clear modifiers that are set */ - clear_flags &= keyboard->modifier_flags; + clear_flags &= modifier_flags; /* Only set modifiers that are currently cleared */ - set_flags &= ~keyboard->modifier_flags; + set_flags &= ~modifier_flags; /* Press/release Shift as needed */ if (set_flags & GUAC_RDP_KEYMAP_MODIFIER_SHIFT) { - guac_rdp_keyboard_update_keysym(keyboard, 0xFFE1, 1, GUAC_RDP_KEY_SOURCE_SYNTHETIC); + guac_rdp_keyboard_update_keysym(keyboard, GUAC_RDP_KEYSYM_LSHIFT, 1, GUAC_RDP_KEY_SOURCE_SYNTHETIC); } else if (clear_flags & GUAC_RDP_KEYMAP_MODIFIER_SHIFT) { - guac_rdp_keyboard_update_keysym(keyboard, 0xFFE1, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); - guac_rdp_keyboard_update_keysym(keyboard, 0xFFE2, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); + guac_rdp_keyboard_update_keysym(keyboard, GUAC_RDP_KEYSYM_LSHIFT, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); + guac_rdp_keyboard_update_keysym(keyboard, GUAC_RDP_KEYSYM_RSHIFT, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); } /* Press/release AltGr as needed */ if (set_flags & GUAC_RDP_KEYMAP_MODIFIER_ALTGR) { - guac_rdp_keyboard_update_keysym(keyboard, 0xFE03, 1, GUAC_RDP_KEY_SOURCE_SYNTHETIC); + guac_rdp_keyboard_update_keysym(keyboard, GUAC_RDP_KEYSYM_ALTGR, 1, GUAC_RDP_KEY_SOURCE_SYNTHETIC); } else if (clear_flags & GUAC_RDP_KEYMAP_MODIFIER_ALTGR) { - guac_rdp_keyboard_update_keysym(keyboard, 0xFFEA, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); - guac_rdp_keyboard_update_keysym(keyboard, 0xFE03, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); + guac_rdp_keyboard_update_keysym(keyboard, GUAC_RDP_KEYSYM_ALTGR, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); + guac_rdp_keyboard_update_keysym(keyboard, GUAC_RDP_KEYSYM_LALT, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); + guac_rdp_keyboard_update_keysym(keyboard, GUAC_RDP_KEYSYM_RALT, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); + guac_rdp_keyboard_update_keysym(keyboard, GUAC_RDP_KEYSYM_LCTRL, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); + guac_rdp_keyboard_update_keysym(keyboard, GUAC_RDP_KEYSYM_RCTRL, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); } } @@ -648,16 +653,9 @@ int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, return 0; } - /* Toggle locks and set modifiers on keydown */ - if (pressed) { + /* Toggle locks on keydown */ + if (pressed) keyboard->lock_flags ^= guac_rdp_keyboard_lock_flag(keysym); - keyboard->modifier_flags |= guac_rdp_keyboard_modifier_flag(keysym); - } - - /* Clear modifiers on keyup */ - else { - keyboard->modifier_flags &= ~guac_rdp_keyboard_modifier_flag(keysym); - } /* If key is known, update state and attempt to send using normal RDP key * events */ diff --git a/src/protocols/rdp/keyboard.h b/src/protocols/rdp/keyboard.h index 425f55c8..300cb252 100644 --- a/src/protocols/rdp/keyboard.h +++ b/src/protocols/rdp/keyboard.h @@ -97,15 +97,6 @@ typedef struct guac_rdp_keyboard { */ guac_client* client; - /** - * The local state of all known modifier keys, as a bitwise OR of the - * modified flags used by the keymaps. - * - * @see GUAC_RDP_KEYMAP_MODIFIER_SHIFT - * @see GUAC_RDP_KEYMAP_MODIFIER_ALTGR - */ - unsigned int modifier_flags; - /** * 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, @@ -207,6 +198,37 @@ void guac_rdp_keyboard_free(guac_rdp_keyboard* keyboard); */ int guac_rdp_keyboard_is_defined(guac_rdp_keyboard* keyboard, int keysym); +/** + * Returns whether the key having the given keysym is currently pressed. + * + * @param keyboard + * The guac_rdp_keyboard instance to check. + * + * @param keysym + * The keysym of the key being checked. + * + * @return + * Non-zero if the key is currently pressed, zero otherwise. + */ +int guac_rdp_keyboard_is_pressed(guac_rdp_keyboard* keyboard, int keysym); + +/** + * Returns the local state of all known modifier keys, as a bitwise OR of the + * modifier flags used by the keymaps. Alternative methods of producing the + * effect of certain modifiers, such as holding Ctrl+Alt for AltGr when a + * dedicated AltGr key is unavailable, are taken into account. + * + * @see GUAC_RDP_KEYMAP_MODIFIER_SHIFT + * @see GUAC_RDP_KEYMAP_MODIFIER_ALTGR + * + * @param keyboard + * The guac_rdp_keyboard associated with the current RDP session. + * + * @return + * The local state of all known modifier keys. + */ +unsigned int guac_rdp_keyboard_get_modifier_flags(guac_rdp_keyboard* keyboard); + /** * 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. diff --git a/src/protocols/rdp/keymap.h b/src/protocols/rdp/keymap.h index 9fa00811..121b85f3 100644 --- a/src/protocols/rdp/keymap.h +++ b/src/protocols/rdp/keymap.h @@ -22,6 +22,61 @@ #include +/** + * The X11 keysym for Num Lock. + */ +#define GUAC_RDP_KEYSYM_NUM_LOCK 0xFF7F + +/** + * The X11 keysym for Scroll Lock. + */ +#define GUAC_RDP_KEYSYM_SCROLL_LOCK 0xFF14 + +/** + * The X11 keysym for Caps Lock. + */ +#define GUAC_RDP_KEYSYM_CAPS_LOCK 0xFFE5 + +/** + * The X11 keysym for Kana Lock. + */ +#define GUAC_RDP_KEYSYM_KANA_LOCK 0xFF2D + +/** + * The X11 keysym for Left Shift. + */ +#define GUAC_RDP_KEYSYM_LSHIFT 0xFFE1 + +/** + * The X11 keysym for Right Shift. + */ +#define GUAC_RDP_KEYSYM_RSHIFT 0xFFE2 + +/** + * The X11 keysym for Left Ctrl. + */ +#define GUAC_RDP_KEYSYM_LCTRL 0xFFE3 + +/** + * The X11 keysym for Right Ctrl. + */ +#define GUAC_RDP_KEYSYM_RCTRL 0xFFE4 + +/** + * The X11 keysym for Left Alt. + */ +#define GUAC_RDP_KEYSYM_LALT 0xFFE9 + +/** + * The X11 keysym for Right Alt. + */ +#define GUAC_RDP_KEYSYM_RALT 0xFFEA + +/** + * The X11 keysym for AltGr. + */ +#define GUAC_RDP_KEYSYM_ALTGR 0xFE03 + /** * Bitwise flag value representing the Shift modifier. */ From 3798d85bd1ff117608aaa4e333182ec38a777b22 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 23 Jun 2020 23:57:43 -0700 Subject: [PATCH 4/5] GUACAMOLE-518: Count client-side pressed keys independently of server-side keys. --- src/protocols/rdp/keyboard.c | 50 ++++++++++++++++++++---------------- src/protocols/rdp/keyboard.h | 6 +++++ 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/protocols/rdp/keyboard.c b/src/protocols/rdp/keyboard.c index 0f1a49ba..52c125b2 100644 --- a/src/protocols/rdp/keyboard.c +++ b/src/protocols/rdp/keyboard.c @@ -645,9 +645,35 @@ int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, } - /* If key is known, ignoring the key event entirely if state is not - * actually changing */ guac_rdp_key* key = guac_rdp_keyboard_get_key(keyboard, keysym); + + /* Update tracking of client-side keyboard state but only for keys which + * are tracked server-side, as well (to ensure that the key count remains + * correct, even if a user sends extra unbalanced or excessive press and + * release events) */ + if (key != NULL && source == GUAC_RDP_KEY_SOURCE_CLIENT) { + + if (pressed && !key->user_pressed) { + keyboard->user_pressed_keys++; + key->user_pressed = 1; + } + else if (!pressed && key->user_pressed) { + + keyboard->user_pressed_keys--; + key->user_pressed = 0; + + /* Reset RDP server keyboard state (releasing any automatically + * pressed keys) once all keys have been released on the client + * side */ + if (keyboard->user_pressed_keys == 0) + guac_rdp_keyboard_reset(keyboard); + + } + + } + + /* If key is known, ignore the key event entirely if state is not actually + * changing */ if (key != NULL) { if ((!pressed && key->pressed == NULL) || (pressed && key->pressed != NULL)) return 0; @@ -672,26 +698,6 @@ int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, guac_rdp_keyboard_send_missing_key(keyboard, keysym); } - if (source == GUAC_RDP_KEY_SOURCE_CLIENT) { - - /* Update tracking of client-side keyboard state but only for keys - * which are tracked server-side, as well (to ensure that the key count - * remains correct, even if a user sends extra unbalanced or excessive - * press and release events) */ - if (key != NULL) { - if (pressed) - keyboard->user_pressed_keys++; - else - keyboard->user_pressed_keys--; - } - - /* Reset RDP server keyboard state (releasing any automatically pressed - * keys) once all keys have been released on the client side */ - if (keyboard->user_pressed_keys == 0) - guac_rdp_keyboard_reset(keyboard); - - } - return 0; } diff --git a/src/protocols/rdp/keyboard.h b/src/protocols/rdp/keyboard.h index 300cb252..44871ca7 100644 --- a/src/protocols/rdp/keyboard.h +++ b/src/protocols/rdp/keyboard.h @@ -84,6 +84,12 @@ typedef struct guac_rdp_key { */ const guac_rdp_keysym_desc* pressed; + /** + * Whether this key is currently pressed by the user, and is included among + * the total tracked by user_pressed_keys within guac_rdp_keyboard. + */ + int user_pressed; + } guac_rdp_key; /** From 628f2fd815ad9d79a97ffb086398836a8a5c7ad0 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 24 Jun 2020 00:50:23 -0700 Subject: [PATCH 5/5] GUACAMOLE-518: Ensure all keys are released even if the key pressed client-side is unknown except through dead keys. --- src/protocols/rdp/keyboard.c | 61 +++++++++++++++++------------------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/src/protocols/rdp/keyboard.c b/src/protocols/rdp/keyboard.c index 52c125b2..b027bde5 100644 --- a/src/protocols/rdp/keyboard.c +++ b/src/protocols/rdp/keyboard.c @@ -651,52 +651,47 @@ int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, * are tracked server-side, as well (to ensure that the key count remains * correct, even if a user sends extra unbalanced or excessive press and * release events) */ - if (key != NULL && source == GUAC_RDP_KEY_SOURCE_CLIENT) { - + if (source == GUAC_RDP_KEY_SOURCE_CLIENT && key != NULL) { if (pressed && !key->user_pressed) { keyboard->user_pressed_keys++; key->user_pressed = 1; } else if (!pressed && key->user_pressed) { - keyboard->user_pressed_keys--; key->user_pressed = 0; + } + } - /* Reset RDP server keyboard state (releasing any automatically - * pressed keys) once all keys have been released on the client - * side */ - if (keyboard->user_pressed_keys == 0) - guac_rdp_keyboard_reset(keyboard); + /* Send events and update server-side lock state only if server-side key + * state is changing (or if server-side state of this key is untracked) */ + if (key == NULL || (pressed && key->pressed == NULL) || (!pressed && key->pressed != NULL)) { + /* Toggle locks on keydown */ + if (pressed) + keyboard->lock_flags ^= guac_rdp_keyboard_lock_flag(keysym); + + /* If key is known, update state and attempt to send using normal RDP key + * events */ + const guac_rdp_keysym_desc* definition = NULL; + if (key != NULL) { + definition = guac_rdp_keyboard_send_defined_key(keyboard, key, pressed); + key->pressed = pressed ? definition : NULL; + } + + /* Fall back to dead keys or Unicode events if otherwise undefined inside + * current keymap (note that we only handle "pressed" here, as neither + * Unicode events nor dead keys can have a pressed/released state) */ + if (definition == NULL && pressed) { + guac_rdp_keyboard_send_missing_key(keyboard, keysym); } } - /* If key is known, ignore the key event entirely if state is not actually - * changing */ - if (key != NULL) { - if ((!pressed && key->pressed == NULL) || (pressed && key->pressed != NULL)) - return 0; - } - - /* Toggle locks on keydown */ - if (pressed) - keyboard->lock_flags ^= guac_rdp_keyboard_lock_flag(keysym); - - /* If key is known, update state and attempt to send using normal RDP key - * events */ - const guac_rdp_keysym_desc* definition = NULL; - if (key != NULL) { - definition = guac_rdp_keyboard_send_defined_key(keyboard, key, pressed); - key->pressed = pressed ? definition : NULL; - } - - /* Fall back to dead keys or Unicode events if otherwise undefined inside - * current keymap (note that we only handle "pressed" here, as neither - * Unicode events nor dead keys can have a pressed/released state) */ - if (definition == NULL && pressed) { - guac_rdp_keyboard_send_missing_key(keyboard, keysym); - } + /* Reset RDP server keyboard state (releasing any automatically + * pressed keys) once all keys have been released on the client + * side */ + if (source == GUAC_RDP_KEY_SOURCE_CLIENT && keyboard->user_pressed_keys == 0) + guac_rdp_keyboard_reset(keyboard); return 0;