diff --git a/src/protocols/rdp/decompose.c b/src/protocols/rdp/decompose.c index 90e00437..d5c91e02 100644 --- a/src/protocols/rdp/decompose.c +++ b/src/protocols/rdp/decompose.c @@ -162,12 +162,12 @@ int guac_rdp_decompose_keysym(guac_rdp_keyboard* keyboard, int keysym) { return 1; /* Press dead key */ - guac_rdp_keyboard_update_keysym(keyboard, key->dead_keysym, 1); - guac_rdp_keyboard_update_keysym(keyboard, key->dead_keysym, 0); + guac_rdp_keyboard_update_keysym(keyboard, key->dead_keysym, 1, GUAC_RDP_KEY_SOURCE_SYNTHETIC); + guac_rdp_keyboard_update_keysym(keyboard, key->dead_keysym, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); /* Press base key */ - guac_rdp_keyboard_update_keysym(keyboard, key->base_keysym, 1); - guac_rdp_keyboard_update_keysym(keyboard, key->base_keysym, 0); + guac_rdp_keyboard_update_keysym(keyboard, key->base_keysym, 1, GUAC_RDP_KEY_SOURCE_SYNTHETIC); + guac_rdp_keyboard_update_keysym(keyboard, key->base_keysym, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); /* Decomposed key successfully typed */ return 0; diff --git a/src/protocols/rdp/input.c b/src/protocols/rdp/input.c index 08b077c3..cb9bb10e 100644 --- a/src/protocols/rdp/input.c +++ b/src/protocols/rdp/input.c @@ -141,7 +141,7 @@ int guac_rdp_user_key_handler(guac_user* user, int keysym, int pressed) { /* Update keysym state */ retval = guac_rdp_keyboard_update_keysym(rdp_client->keyboard, - keysym, pressed); + keysym, pressed, GUAC_RDP_KEY_SOURCE_CLIENT); complete: pthread_rwlock_unlock(&(rdp_client->lock)); diff --git a/src/protocols/rdp/keyboard.c b/src/protocols/rdp/keyboard.c index 1c1c9b60..5bb925bf 100644 --- a/src/protocols/rdp/keyboard.c +++ b/src/protocols/rdp/keyboard.c @@ -607,25 +607,25 @@ void guac_rdp_keyboard_update_modifiers(guac_rdp_keyboard* keyboard, /* Press/release Shift as needed */ if (set_flags & GUAC_RDP_KEYMAP_MODIFIER_SHIFT) { - guac_rdp_keyboard_update_keysym(keyboard, 0xFFE1, 1); + guac_rdp_keyboard_update_keysym(keyboard, 0xFFE1, 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_keyboard_update_keysym(keyboard, 0xFFE2, 0); + 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); } /* Press/release AltGr as needed */ if (set_flags & GUAC_RDP_KEYMAP_MODIFIER_ALTGR) { - guac_rdp_keyboard_update_keysym(keyboard, 0xFE03, 1); + 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, 0xFE03, 0); + guac_rdp_keyboard_update_keysym(keyboard, 0xFE03, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); } } int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, - int keysym, int pressed) { + int keysym, int pressed, guac_rdp_key_source source) { /* Synchronize lock keys states, if this has not yet been done */ if (!keyboard->synchronized) { @@ -673,7 +673,39 @@ 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; } +void guac_rdp_keyboard_reset(guac_rdp_keyboard* keyboard) { + + /* Release all pressed keys */ + for (int i = 0; i < keyboard->num_keys; i++) { + guac_rdp_key* key = &keyboard->keys[i]; + if (key->pressed != NULL) + guac_rdp_keyboard_update_keysym(keyboard, key->pressed->keysym, 0, + GUAC_RDP_KEY_SOURCE_SYNTHETIC); + } + +} + diff --git a/src/protocols/rdp/keyboard.h b/src/protocols/rdp/keyboard.h index 94888160..a07312bf 100644 --- a/src/protocols/rdp/keyboard.h +++ b/src/protocols/rdp/keyboard.h @@ -37,6 +37,24 @@ */ #define GUAC_RDP_KEY_MAX_DEFINITIONS 4 +/** + * All possible sources of RDP key events tracked by guac_rdp_keyboard. + */ +typedef enum guac_rdp_key_source { + + /** + * The key event was received directly from the Guacamole client via a + * "key" instruction. + */ + GUAC_RDP_KEY_SOURCE_CLIENT = 0, + + /** + * The key event is being synthesized internally within the RDP support. + */ + GUAC_RDP_KEY_SOURCE_SYNTHETIC = 1 + +} guac_rdp_key_source; + /** * A representation of a single key within the overall local keyboard, * including the definition of that key within the RDP server's keymap and @@ -130,6 +148,14 @@ typedef struct guac_rdp_keyboard { */ guac_rdp_key* keys_by_keysym[0x20000]; + /** + * The total number of keys that the user of the connection is currently + * holding down. This value indicates only the client-side keyboard state. + * It DOES NOT indicate the number of keys currently pressed within the RDP + * server. + */ + int user_pressed_keys; + } guac_rdp_keyboard; /** @@ -234,11 +260,24 @@ void guac_rdp_keyboard_update_modifiers(guac_rdp_keyboard* keyboard, * @param pressed * Zero if the keysym is being released, non-zero otherwise. * + * @param source + * The source of the key event represented by this call to + * guac_rdp_keyboard_update_keysym(). + * * @return * Zero if the keys were successfully sent, non-zero otherwise. */ int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, - int keysym, int pressed); + int keysym, int pressed, guac_rdp_key_source source); + +/** + * Releases all currently pressed keys, sending key release events to the RDP + * server as necessary. Lock states (Caps Lock, etc.) are not affected. + * + * @param keyboard + * The guac_rdp_keyboard associated with the current RDP session. + */ +void guac_rdp_keyboard_reset(guac_rdp_keyboard* keyboard); #endif