GUACAMOLE-518: Automatically release any automatically-pressed keys after user has released all keys on the client side.

This commit is contained in:
Michael Jumper 2020-06-22 15:14:27 -07:00
parent cb6ffd06e6
commit 48b3d5038f
4 changed files with 83 additions and 12 deletions

View File

@ -162,12 +162,12 @@ int guac_rdp_decompose_keysym(guac_rdp_keyboard* keyboard, int keysym) {
return 1; return 1;
/* Press dead key */ /* Press dead key */
guac_rdp_keyboard_update_keysym(keyboard, key->dead_keysym, 1); 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_keyboard_update_keysym(keyboard, key->dead_keysym, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC);
/* Press base key */ /* Press base key */
guac_rdp_keyboard_update_keysym(keyboard, key->base_keysym, 1); 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_keyboard_update_keysym(keyboard, key->base_keysym, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC);
/* Decomposed key successfully typed */ /* Decomposed key successfully typed */
return 0; return 0;

View File

@ -141,7 +141,7 @@ int guac_rdp_user_key_handler(guac_user* user, int keysym, int pressed) {
/* Update keysym state */ /* Update keysym state */
retval = guac_rdp_keyboard_update_keysym(rdp_client->keyboard, retval = guac_rdp_keyboard_update_keysym(rdp_client->keyboard,
keysym, pressed); keysym, pressed, GUAC_RDP_KEY_SOURCE_CLIENT);
complete: complete:
pthread_rwlock_unlock(&(rdp_client->lock)); pthread_rwlock_unlock(&(rdp_client->lock));

View File

@ -607,25 +607,25 @@ void guac_rdp_keyboard_update_modifiers(guac_rdp_keyboard* keyboard,
/* Press/release Shift as needed */ /* Press/release Shift as needed */
if (set_flags & GUAC_RDP_KEYMAP_MODIFIER_SHIFT) { 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) { else if (clear_flags & GUAC_RDP_KEYMAP_MODIFIER_SHIFT) {
guac_rdp_keyboard_update_keysym(keyboard, 0xFFE1, 0); guac_rdp_keyboard_update_keysym(keyboard, 0xFFE1, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC);
guac_rdp_keyboard_update_keysym(keyboard, 0xFFE2, 0); guac_rdp_keyboard_update_keysym(keyboard, 0xFFE2, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC);
} }
/* Press/release AltGr as needed */ /* Press/release AltGr as needed */
if (set_flags & GUAC_RDP_KEYMAP_MODIFIER_ALTGR) { 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) { 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 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 */ /* Synchronize lock keys states, if this has not yet been done */
if (!keyboard->synchronized) { 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); 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; 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);
}
}

View File

@ -37,6 +37,24 @@
*/ */
#define GUAC_RDP_KEY_MAX_DEFINITIONS 4 #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, * A representation of a single key within the overall local keyboard,
* including the definition of that key within the RDP server's keymap and * 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]; 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; } guac_rdp_keyboard;
/** /**
@ -234,11 +260,24 @@ void guac_rdp_keyboard_update_modifiers(guac_rdp_keyboard* keyboard,
* @param pressed * @param pressed
* Zero if the keysym is being released, non-zero otherwise. * 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 * @return
* Zero if the keys were successfully sent, non-zero otherwise. * Zero if the keys were successfully sent, non-zero otherwise.
*/ */
int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, 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 #endif