From ce0982fefd1243d86dea2b668db4e8985362c867 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 21 Jun 2020 20:59:11 -0700 Subject: [PATCH 01/22] GUACAMOLE-518: Track and update modifier states based on overall flags, not keysyms. --- src/protocols/rdp/keyboard.c | 140 ++++++++++++++++---------- src/protocols/rdp/keyboard.h | 56 ++++++----- src/protocols/rdp/keymap.h | 102 +++++++------------ src/protocols/rdp/keymaps/generate.pl | 48 ++++----- 4 files changed, 169 insertions(+), 177 deletions(-) diff --git a/src/protocols/rdp/keyboard.c b/src/protocols/rdp/keyboard.c index 42cc4db6..24fcecbc 100644 --- a/src/protocols/rdp/keyboard.c +++ b/src/protocols/rdp/keyboard.c @@ -28,6 +28,39 @@ #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 @@ -328,38 +361,15 @@ int guac_rdp_keyboard_send_event(guac_rdp_keyboard* 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, - keysym_desc->set_keysyms, - GUAC_RDP_KEY_RELEASED, - GUAC_RDP_KEY_PRESSED); - - /* If defined, release any keys that must be cleared */ - if (keysym_desc->clear_keysyms != NULL) - guac_rdp_keyboard_send_events(keyboard, - keysym_desc->clear_keysyms, - GUAC_RDP_KEY_PRESSED, - GUAC_RDP_KEY_RELEASED); + /* Update remote modifier states as necessary */ + guac_rdp_keyboard_update_modifiers(keyboard, + keysym_desc->set_modifiers, + keysym_desc->clear_modifiers); /* Fire actual key event for target key */ guac_rdp_send_key_event(rdp_client, keysym_desc->scancode, keysym_desc->flags, pressed); - /* If defined, release any keys that were originally released */ - if (keysym_desc->set_keysyms != NULL) - guac_rdp_keyboard_send_events(keyboard, - keysym_desc->set_keysyms, - GUAC_RDP_KEY_RELEASED, - GUAC_RDP_KEY_RELEASED); - - /* If defined, send any keys that were originally set */ - if (keysym_desc->clear_keysyms != NULL) - guac_rdp_keyboard_send_events(keyboard, - keysym_desc->clear_keysyms, - GUAC_RDP_KEY_PRESSED, - GUAC_RDP_KEY_PRESSED); - return 0; } @@ -398,28 +408,6 @@ int guac_rdp_keyboard_send_event(guac_rdp_keyboard* keyboard, return 0; } -void guac_rdp_keyboard_send_events(guac_rdp_keyboard* keyboard, - const int* keysym_string, guac_rdp_key_state from, - guac_rdp_key_state to) { - - int keysym; - - /* Send all keysyms in string, NULL terminated */ - while ((keysym = *keysym_string) != 0) { - - /* If key is currently in given state, send event for changing it to - * specified "to" state */ - guac_rdp_key* key = guac_rdp_keyboard_get_key(keyboard, keysym); - if (key != NULL && key->state == from) - guac_rdp_keyboard_send_event(keyboard, *keysym_string, to); - - /* Next keysym */ - keysym_string++; - - } - -} - void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard, int set_flags, int clear_flags) { @@ -437,9 +425,50 @@ void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard, } +void guac_rdp_keyboard_update_modifiers(guac_rdp_keyboard* keyboard, + int set_flags, int clear_flags) { + + /* Only clear modifiers that are set */ + clear_flags &= keyboard->modifier_flags; + + /* Only set modifiers that are currently cleared */ + set_flags &= ~keyboard->modifier_flags; + + /* Press/release Shift as needed */ + if (set_flags & GUAC_RDP_KEYMAP_MODIFIER_SHIFT) { + guac_rdp_keyboard_update_keysym(keyboard, 0xFFE1, 1); + } + 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); + } + + /* Press/release AltGr as needed */ + if (set_flags & GUAC_RDP_KEYMAP_MODIFIER_ALTGR) { + guac_rdp_keyboard_update_keysym(keyboard, 0xFE03, 1); + } + else if (clear_flags & GUAC_RDP_KEYMAP_MODIFIER_ALTGR) { + guac_rdp_keyboard_update_keysym(keyboard, 0xFE03, 0); + } + +} + int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, int keysym, int pressed) { + guac_rdp_key* key = guac_rdp_keyboard_get_key(keyboard, keysym); + if (key != NULL) { + + /* Ignore event if state is not changing */ + guac_rdp_key_state new_state = pressed ? GUAC_RDP_KEY_PRESSED : GUAC_RDP_KEY_RELEASED; + if (key->state == new_state) + return 0; + + /* Update keysym state */ + key->state = new_state; + + } + /* Synchronize lock keys states, if this has not yet been done */ if (!keyboard->synchronized) { @@ -452,14 +481,15 @@ int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, } - /* Toggle lock flag, if any */ - if (pressed) + /* Toggle locks and set modifiers on keydown */ + if (pressed) { keyboard->lock_flags ^= guac_rdp_keyboard_lock_flag(keysym); + keyboard->modifier_flags |= guac_rdp_keyboard_modifier_flag(keysym); + } - /* Update keysym state */ - guac_rdp_key* key = guac_rdp_keyboard_get_key(keyboard, keysym); - if (key != NULL) - key->state = pressed ? GUAC_RDP_KEY_PRESSED : GUAC_RDP_KEY_RELEASED; + /* Clear modifiers on keyup */ + else + keyboard->modifier_flags &= ~guac_rdp_keyboard_modifier_flag(keysym); return guac_rdp_keyboard_send_event(keyboard, keysym, pressed); diff --git a/src/protocols/rdp/keyboard.h b/src/protocols/rdp/keyboard.h index 6296a71d..12306842 100644 --- a/src/protocols/rdp/keyboard.h +++ b/src/protocols/rdp/keyboard.h @@ -79,6 +79,15 @@ 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 + */ + 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, @@ -177,33 +186,6 @@ int guac_rdp_keyboard_is_defined(guac_rdp_keyboard* keyboard, int keysym); int guac_rdp_keyboard_send_event(guac_rdp_keyboard* keyboard, int keysym, int pressed); -/** - * For every keysym in the given NULL-terminated array of keysyms, send the RDP - * key events required to update the remote state of those keys as specified, - * depending on the current local state of those keysyms. For each key in the - * "from" state, that key will be updated to the "to" state. The locally-stored - * state of each key is remains untouched. - * - * @param keyboard - * The guac_rdp_keyboard associated with the current RDP session. - * - * @param keysym_string - * A NULL-terminated array of keysyms, each of which will be updated. - * - * @param from - * GUAC_RDP_KEY_RELEASED if the state of currently-released keys should be - * updated, or GUAC_RDP_KEY_PRESSED if the state of currently-pressed keys - * should be updated. - * - * @param to - * GUAC_RDP_KEY_RELEASED if the keys being updated should be marked as - * released, or GUAC_RDP_KEY_PRESSED if the keys being updated should be - * marked as pressed. - */ -void guac_rdp_keyboard_send_events(guac_rdp_keyboard* keyboard, - const int* keysym_string, guac_rdp_key_state from, - guac_rdp_key_state 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. @@ -224,6 +206,26 @@ 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); +/** + * Updates the local state of the modifier keys (such as Shift or AltGr), + * synchronizing the remote state of those keys if it is expected to differ. + * Valid modifier flags are defined by keymap.h. + * + * @see GUAC_RDP_KEYMAP_MODIFIER_SHIFT + * @see GUAC_RDP_KEYMAP_MODIFIER_ALTGR + * + * @param keyboard + * The guac_rdp_keyboard associated with the current RDP session. + * + * @param set_flags + * The modifier key flags which should be set. + * + * @param clear_flags + * The modifier key flags which should be cleared. + */ +void guac_rdp_keyboard_update_modifiers(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/keymap.h b/src/protocols/rdp/keymap.h index bac1a7fa..5910290b 100644 --- a/src/protocols/rdp/keymap.h +++ b/src/protocols/rdp/keymap.h @@ -22,6 +22,16 @@ #include +/** + * Bitwise flag value representing the Shift modifier. + */ +#define GUAC_RDP_KEYMAP_MODIFIER_SHIFT 1 + +/** + * Bitwise flag value representing the AltGr modifier. + */ +#define GUAC_RDP_KEYMAP_MODIFIER_ALTGR 2 + /** * Represents a keysym-to-scancode mapping for RDP, with extra information * about the state of prerequisite keysyms. @@ -39,29 +49,44 @@ typedef struct guac_rdp_keysym_desc { int scancode; /** - * Required RDP-specific flags. + * Required RDP-specific flags that must be sent along with the scancode. */ int flags; /** - * Null-terminated list of keysyms which must be down for this keysym - * to be properly typed. + * Bitwise-OR of the flags of any modifiers that must be active for the + * associated scancode to be interpreted as this keysym. + * + * If the associated keysym is pressed, and any of these modifiers are not + * currently active, Guacamole's RDP support must send additional events to + * activate these modifiers prior to sending the scancode for this keysym. + * + * @see GUAC_RDP_KEYMAP_MODIFIER_SHIFT + * @see GUAC_RDP_KEYMAP_MODIFIER_ALTGR */ - const int* set_keysyms; + const int set_modifiers; /** - * Null-terminated list of keysyms which must be up for this keysym - * to be properly typed. + * Bitwise-OR of the flags of any modifiers that must NOT be active for the + * associated scancode to be interpreted as this keysym. + * + * If the associated keysym is pressed, and any of these modifiers are + * currently active, Guacamole's RDP support must send additional events to + * deactivate these modifiers prior to sending the scancode for this + * keysym. + * + * @see GUAC_RDP_KEYMAP_MODIFIER_SHIFT + * @see GUAC_RDP_KEYMAP_MODIFIER_ALTGR */ - const int* clear_keysyms; + const int clear_modifiers; /** * 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; + */ + const int set_locks; /** * Bitwise OR of the flags of all lock keys (ie: Caps lock, Num lock, etc.) @@ -69,7 +94,7 @@ typedef struct guac_rdp_keysym_desc { * are KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and * KBD_SYNC_KANA_LOCK. */ - int clear_locks; + const int clear_locks; } guac_rdp_keysym_desc; @@ -110,63 +135,6 @@ struct guac_rdp_keymap { */ #define GUAC_DEFAULT_KEYMAP "en-us-qwerty" -/** - * Keysym string containing only the left "shift" key. - */ -extern const int GUAC_KEYSYMS_SHIFT[]; - -/** - * Keysym string containing both "shift" keys. - */ -extern const int GUAC_KEYSYMS_ALL_SHIFT[]; - -/** - * Keysym string containing only the right "alt" key (AltGr). - */ -extern const int GUAC_KEYSYMS_ALTGR[]; - -/** - * Keysym string containing the right "alt" key (AltGr) and - * left shift. - */ -extern const int GUAC_KEYSYMS_SHIFT_ALTGR[]; - -/** - * Keysym string containing the right "alt" key (AltGr) and - * both shift keys. - */ -extern const int GUAC_KEYSYMS_ALL_SHIFT_ALTGR[]; - -/** - * Keysym string containing only the left "ctrl" key. - */ -extern const int GUAC_KEYSYMS_CTRL[]; - -/** - * Keysym string containing both "ctrl" keys. - */ -extern const int GUAC_KEYSYMS_ALL_CTRL[]; - -/** - * Keysym string containing only the left "alt" key. - */ -extern const int GUAC_KEYSYMS_ALT[]; - -/** - * Keysym string containing both "alt" keys. - */ -extern const int GUAC_KEYSYMS_ALL_ALT[]; - -/** - * Keysym string containing the left "alt" and left "ctrl" keys - */ -extern const int GUAC_KEYSYMS_CTRL_ALT[]; - -/** - * Keysym string containing all modifier keys. - */ -extern const int GUAC_KEYSYMS_ALL_MODIFIERS[]; - /** * NULL-terminated array of all keymaps. */ diff --git a/src/protocols/rdp/keymaps/generate.pl b/src/protocols/rdp/keymaps/generate.pl index f1059a75..446de9a2 100755 --- a/src/protocols/rdp/keymaps/generate.pl +++ b/src/protocols/rdp/keymaps/generate.pl @@ -92,10 +92,12 @@ for my $filename (@ARGV) { my $ext_flags = 0; my $set_shift = 0; my $set_altgr = 0; + my $set_caps = 0; my $set_num = 0; my $clear_shift = 0; my $clear_altgr = 0; + my $clear_caps = 0; my $clear_num = 0; # Parse ranges and options @@ -105,6 +107,7 @@ for my $filename (@ARGV) { if ((my $opt) = m/^\+([a-z]+)$/) { if ($opt eq "shift") { $set_shift = 1; } elsif ($opt eq "altgr") { $set_altgr = 1; } + elsif ($opt eq "caps") { $set_caps = 1; } elsif ($opt eq "num") { $set_num = 1; } elsif ($opt eq "ext") { $ext_flags = 1; } else { @@ -117,6 +120,7 @@ for my $filename (@ARGV) { elsif ((my $opt) = m/^-([a-z]+)$/) { if ($opt eq "shift") { $clear_shift = 1; } elsif ($opt eq "altgr") { $clear_altgr = 1; } + elsif ($opt eq "caps") { $clear_caps = 1; } elsif ($opt eq "num") { $clear_num = 1; } else { die "$filename: $.: ERROR: " @@ -175,37 +179,25 @@ for my $filename (@ARGV) { . " .keysym = " . $keysyms[$i] . "," . " .scancode = " . $scancodes[$i]; - # Set requirements - if ($set_shift && !$set_altgr) { - $content .= ", .set_keysyms = GUAC_KEYSYMS_SHIFT"; - } - elsif (!$set_shift && $set_altgr) { - $content .= ", .set_keysyms = GUAC_KEYSYMS_ALTGR"; - } - elsif ($set_shift && $set_altgr) { - $content .= ", .set_keysyms = GUAC_KEYSYMS_SHIFT_ALTGR"; - } + # Modifiers that must be active + $content .= ", .set_modifiers = 0"; + $content .= " | GUAC_RDP_KEYMAP_MODIFIER_SHIFT" if $set_shift; + $content .= " | GUAC_RDP_KEYMAP_MODIFIER_ALTGR" if $set_altgr; - # Clear requirements - if ($clear_shift && !$clear_altgr) { - $content .= ", .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT"; - } - elsif (!$clear_shift && $clear_altgr) { - $content .= ", .clear_keysyms = GUAC_KEYSYMS_ALTGR"; - } - elsif ($clear_shift && $clear_altgr) { - $content .= ", .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR"; - } + # Modifiers that must be inactive + $content .= ", .clear_modifiers = 0"; + $content .= " | GUAC_RDP_KEYMAP_MODIFIER_SHIFT" if $clear_shift; + $content .= " | GUAC_RDP_KEYMAP_MODIFIER_ALTGR" if $clear_altgr; - # Set locks - if ($set_num) { - $content .= ", .set_locks = KBD_SYNC_NUM_LOCK"; - } + # Locks that must be set + $content .= ", .set_locks = 0"; + $content .= " | KBD_SYNC_NUM_LOCK" if $set_num; + $content .= " | KBD_SYNC_CAPS_LOCK" if $set_caps; - # Clear locks - if ($clear_num) { - $content .= ", .clear_locks = KBD_SYNC_NUM_LOCK"; - } + # Locks that must NOT be set + $content .= ", .clear_locks = 0"; + $content .= " | KBD_SYNC_NUM_LOCK" if $clear_num; + $content .= " | KBD_SYNC_CAPS_LOCK" if $clear_caps; # Flags if ($ext_flags) { From 7fd54c56a84f7e1ec8efa33244a17b5e46137663 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 21 Jun 2020 21:03:41 -0700 Subject: [PATCH 02/22] GUACAMOLE-518: Modifications to generate.pl should result in regeneration of all keymaps. --- src/protocols/rdp/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am index ff6a93d8..ad949b89 100644 --- a/src/protocols/rdp/Makefile.am +++ b/src/protocols/rdp/Makefile.am @@ -241,7 +241,7 @@ rdp_keymaps = \ $(srcdir)/keymaps/da_dk_qwerty.keymap \ $(srcdir)/keymaps/tr_tr_qwerty.keymap -_generated_keymaps.c: $(rdp_keymaps) +_generated_keymaps.c: $(rdp_keymaps) $(srcdir)/keymaps/generate.pl $(AM_V_GEN) $(srcdir)/keymaps/generate.pl $(rdp_keymaps) _generated_channel_entry_wrappers.c: $(srcdir)/plugins/channels.h $(srcdir)/plugins/generate-entry-wrappers.pl From 96c4c208b48adafa57dc1f292edb04dafb1aecbe Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 21 Jun 2020 22:10:02 -0700 Subject: [PATCH 03/22] GUACAMOLE-518: Ensure keyboard state is always updated for all keys pressed/released. --- src/protocols/rdp/decompose.c | 8 +- src/protocols/rdp/keyboard.c | 176 ++++++++++++++++++++-------------- src/protocols/rdp/keyboard.h | 20 ---- 3 files changed, 109 insertions(+), 95 deletions(-) diff --git a/src/protocols/rdp/decompose.c b/src/protocols/rdp/decompose.c index e42a7697..90e00437 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_send_event(keyboard, key->dead_keysym, 1); - guac_rdp_keyboard_send_event(keyboard, key->dead_keysym, 0); + guac_rdp_keyboard_update_keysym(keyboard, key->dead_keysym, 1); + guac_rdp_keyboard_update_keysym(keyboard, key->dead_keysym, 0); /* Press base key */ - guac_rdp_keyboard_send_event(keyboard, key->base_keysym, 1); - guac_rdp_keyboard_send_event(keyboard, key->base_keysym, 0); + guac_rdp_keyboard_update_keysym(keyboard, key->base_keysym, 1); + guac_rdp_keyboard_update_keysym(keyboard, key->base_keysym, 0); /* Decomposed key successfully typed */ return 0; diff --git a/src/protocols/rdp/keyboard.c b/src/protocols/rdp/keyboard.c index 24fcecbc..dd20b0a8 100644 --- a/src/protocols/rdp/keyboard.c +++ b/src/protocols/rdp/keyboard.c @@ -340,72 +340,95 @@ int guac_rdp_keyboard_is_defined(guac_rdp_keyboard* keyboard, int keysym) { } -int guac_rdp_keyboard_send_event(guac_rdp_keyboard* keyboard, - int keysym, int pressed) { +/** + * Presses/releases the requested key by sending one or more RDP key events, as + * defined within the keymap defining that key. + * + * @param keyboard + * The guac_rdp_keyboard associated with the current RDP session. + * + * @param key + * The guac_rdp_keysym_desc of the key being pressed or released, as + * retrieved from the relevant keymap. + * + * @param pressed + * Zero if the key is being released, non-zero otherwise. + * + * @return + * Zero if the key was successfully pressed/released, non-zero if the key + * cannot be sent using RDP key events. + */ +static int guac_rdp_keyboard_send_defined_key(guac_rdp_keyboard* keyboard, + guac_rdp_key* key, int pressed) { guac_client* client = keyboard->client; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; - /* If keysym is actually defined within keyboard */ - guac_rdp_key* key = guac_rdp_keyboard_get_key(keyboard, keysym); - if (key != NULL) { + if (key->definition->scancode == 0) + return 1; - /* Look up scancode mapping */ - const guac_rdp_keysym_desc* keysym_desc = key->definition; + const guac_rdp_keysym_desc* keysym_desc = key->definition; - /* 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); - - /* Update remote modifier states as necessary */ - guac_rdp_keyboard_update_modifiers(keyboard, - keysym_desc->set_modifiers, - keysym_desc->clear_modifiers); - - /* Fire actual key event for target key */ - guac_rdp_send_key_event(rdp_client, keysym_desc->scancode, - keysym_desc->flags, pressed); - - return 0; - - } - } - - /* 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) */ + /* Update state of required locks and modifiers only when key is just + * now being pressed */ if (pressed) { + guac_rdp_keyboard_update_locks(keyboard, + keysym_desc->set_locks, + keysym_desc->clear_locks); - /* Attempt to type using dead keys */ - if (!guac_rdp_decompose_keysym(keyboard, keysym)) - return 0; - - guac_client_log(client, GUAC_LOG_DEBUG, - "Sending keysym 0x%x as Unicode", keysym); - - /* Translate keysym into codepoint */ - int codepoint; - if (keysym <= 0xFF) - codepoint = keysym; - else if (keysym >= 0x1000000) - codepoint = keysym & 0xFFFFFF; - else { - guac_client_log(client, GUAC_LOG_DEBUG, - "Unmapped keysym has no equivalent unicode " - "value: 0x%x", keysym); - return 0; - } - - /* Send as Unicode event */ - guac_rdp_send_unicode_event(rdp_client, codepoint); - + guac_rdp_keyboard_update_modifiers(keyboard, + keysym_desc->set_modifiers, + keysym_desc->clear_modifiers); } - + + /* Fire actual key event for target key */ + guac_rdp_send_key_event(rdp_client, keysym_desc->scancode, + keysym_desc->flags, pressed); + return 0; + +} + +/** + * Presses and releases the requested key by sending one or more RDP events, + * without relying on a keymap for that key. This will typically involve either + * sending the key using a Unicode event or decomposing the key into a series + * of keypresses involving deadkeys. + * + * @param keyboard + * The guac_rdp_keyboard associated with the current RDP session. + * + * @param keysym + * The keysym of the key to press and release. + */ +static void guac_rdp_keyboard_send_missing_key(guac_rdp_keyboard* keyboard, + int keysym) { + + guac_client* client = keyboard->client; + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + + /* Attempt to type using dead keys */ + if (!guac_rdp_decompose_keysym(keyboard, keysym)) + return; + + guac_client_log(client, GUAC_LOG_DEBUG, "Sending keysym 0x%x as " + "Unicode", keysym); + + /* Translate keysym into codepoint */ + int codepoint; + if (keysym <= 0xFF) + codepoint = keysym; + else if (keysym >= 0x1000000) + codepoint = keysym & 0xFFFFFF; + else { + guac_client_log(client, GUAC_LOG_DEBUG, "Unmapped keysym has no " + "equivalent unicode value: 0x%x", keysym); + return; + } + + /* Send as Unicode event */ + guac_rdp_send_unicode_event(rdp_client, codepoint); + } void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard, @@ -456,19 +479,6 @@ void guac_rdp_keyboard_update_modifiers(guac_rdp_keyboard* keyboard, int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, int keysym, int pressed) { - guac_rdp_key* key = guac_rdp_keyboard_get_key(keyboard, keysym); - if (key != NULL) { - - /* Ignore event if state is not changing */ - guac_rdp_key_state new_state = pressed ? GUAC_RDP_KEY_PRESSED : GUAC_RDP_KEY_RELEASED; - if (key->state == new_state) - return 0; - - /* Update keysym state */ - key->state = new_state; - - } - /* Synchronize lock keys states, if this has not yet been done */ if (!keyboard->synchronized) { @@ -481,6 +491,19 @@ int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, } + /* If key is known, update stored state, ignoring the key event entirely if + * state is not actually changing */ + guac_rdp_key* key = guac_rdp_keyboard_get_key(keyboard, keysym); + if (key != NULL) { + + guac_rdp_key_state new_state = pressed ? GUAC_RDP_KEY_PRESSED : GUAC_RDP_KEY_RELEASED; + if (key->state == new_state) + return 0; + + key->state = new_state; + + } + /* Toggle locks and set modifiers on keydown */ if (pressed) { keyboard->lock_flags ^= guac_rdp_keyboard_lock_flag(keysym); @@ -488,10 +511,21 @@ int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, } /* Clear modifiers on keyup */ - else + else { keyboard->modifier_flags &= ~guac_rdp_keyboard_modifier_flag(keysym); + } - return guac_rdp_keyboard_send_event(keyboard, keysym, pressed); + /* Attempt to send using normal RDP key events */ + if (key == NULL && !guac_rdp_keyboard_send_defined_key(keyboard, key, pressed)) + return 0; + + /* 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 (pressed) + guac_rdp_keyboard_send_missing_key(keyboard, keysym); + + return 0; } diff --git a/src/protocols/rdp/keyboard.h b/src/protocols/rdp/keyboard.h index 12306842..f2ab5bc7 100644 --- a/src/protocols/rdp/keyboard.h +++ b/src/protocols/rdp/keyboard.h @@ -166,26 +166,6 @@ void guac_rdp_keyboard_free(guac_rdp_keyboard* keyboard); */ int guac_rdp_keyboard_is_defined(guac_rdp_keyboard* keyboard, int keysym); -/** - * Sends one or more RDP key events, effectively pressing or releasing the - * given keysym on the remote side. The key events sent will depend on the - * current keymap. The locally-stored state of each key is remains untouched. - * - * @param keyboard - * The guac_rdp_keyboard associated with the current RDP session. - * - * @param keysym - * The keysym being pressed or released. - * - * @param pressed - * Zero if the keysym is being released, non-zero otherwise. - * - * @return - * Zero if the keys were successfully sent, non-zero otherwise. - */ -int guac_rdp_keyboard_send_event(guac_rdp_keyboard* keyboard, - int keysym, int pressed); - /** * 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. From 67450d89f3646a52e58f5110bc8939782e473e7d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 10:03:07 -0700 Subject: [PATCH 04/22] GUACAMOLE-518: Correct signedness of keyboard flag variables. --- src/protocols/rdp/keyboard.c | 8 ++++---- src/protocols/rdp/keyboard.h | 8 ++++---- src/protocols/rdp/keymap.h | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/protocols/rdp/keyboard.c b/src/protocols/rdp/keyboard.c index dd20b0a8..d2294237 100644 --- a/src/protocols/rdp/keyboard.c +++ b/src/protocols/rdp/keyboard.c @@ -186,7 +186,7 @@ static void guac_rdp_send_unicode_event(guac_rdp_client* rdp_client, * lock keys will be active. */ static void guac_rdp_send_synchronize_event(guac_rdp_client* rdp_client, - int flags) { + UINT32 flags) { /* Skip if not yet connected */ freerdp* rdp_inst = rdp_client->rdp_inst; @@ -432,13 +432,13 @@ static void guac_rdp_keyboard_send_missing_key(guac_rdp_keyboard* keyboard, } void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard, - int set_flags, int clear_flags) { + unsigned int set_flags, unsigned 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; + unsigned 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) { @@ -449,7 +449,7 @@ void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard, } void guac_rdp_keyboard_update_modifiers(guac_rdp_keyboard* keyboard, - int set_flags, int clear_flags) { + unsigned int set_flags, unsigned int clear_flags) { /* Only clear modifiers that are set */ clear_flags &= keyboard->modifier_flags; diff --git a/src/protocols/rdp/keyboard.h b/src/protocols/rdp/keyboard.h index f2ab5bc7..e8b28a62 100644 --- a/src/protocols/rdp/keyboard.h +++ b/src/protocols/rdp/keyboard.h @@ -86,14 +86,14 @@ typedef struct guac_rdp_keyboard { * @see GUAC_RDP_KEYMAP_MODIFIER_SHIFT * @see GUAC_RDP_KEYMAP_MODIFIER_ALTGR */ - int modifier_flags; + 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, * KBD_SYNC_CAPS_LOCK, and KBD_SYNC_KANA_LOCK. */ - int lock_flags; + UINT32 lock_flags; /** * Whether the states of remote lock keys (Caps lock, Num lock, etc.) have @@ -184,7 +184,7 @@ int guac_rdp_keyboard_is_defined(guac_rdp_keyboard* keyboard, int keysym); * KBD_SYNC_KANA_LOCK. */ void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard, - int set_flags, int clear_flags); + unsigned int set_flags, unsigned int clear_flags); /** * Updates the local state of the modifier keys (such as Shift or AltGr), @@ -204,7 +204,7 @@ void guac_rdp_keyboard_update_locks(guac_rdp_keyboard* keyboard, * The modifier key flags which should be cleared. */ void guac_rdp_keyboard_update_modifiers(guac_rdp_keyboard* keyboard, - int set_flags, int clear_flags); + unsigned int set_flags, unsigned int clear_flags); /** * Updates the local state of the given keysym, sending the key events required diff --git a/src/protocols/rdp/keymap.h b/src/protocols/rdp/keymap.h index 5910290b..9fa00811 100644 --- a/src/protocols/rdp/keymap.h +++ b/src/protocols/rdp/keymap.h @@ -64,7 +64,7 @@ typedef struct guac_rdp_keysym_desc { * @see GUAC_RDP_KEYMAP_MODIFIER_SHIFT * @see GUAC_RDP_KEYMAP_MODIFIER_ALTGR */ - const int set_modifiers; + const unsigned int set_modifiers; /** * Bitwise-OR of the flags of any modifiers that must NOT be active for the @@ -78,7 +78,7 @@ typedef struct guac_rdp_keysym_desc { * @see GUAC_RDP_KEYMAP_MODIFIER_SHIFT * @see GUAC_RDP_KEYMAP_MODIFIER_ALTGR */ - const int clear_modifiers; + const unsigned int clear_modifiers; /** * Bitwise OR of the flags of all lock keys (ie: Caps lock, Num lock, etc.) @@ -86,7 +86,7 @@ typedef struct guac_rdp_keysym_desc { * are KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and * KBD_SYNC_KANA_LOCK. */ - const int set_locks; + const unsigned int set_locks; /** * Bitwise OR of the flags of all lock keys (ie: Caps lock, Num lock, etc.) @@ -94,7 +94,7 @@ typedef struct guac_rdp_keysym_desc { * are KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK, and * KBD_SYNC_KANA_LOCK. */ - const int clear_locks; + const unsigned int clear_locks; } guac_rdp_keysym_desc; From cb6ffd06e65d903f89bd25b7d69e425f0363a3bc Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 14:47:09 -0700 Subject: [PATCH 05/22] GUACAMOLE-518: Allow multiple possible definitions (means of typing a particular key) for each keysym. --- src/protocols/rdp/keyboard.c | 264 +++++++++++++++++++++++++++-------- src/protocols/rdp/keyboard.h | 62 ++++---- 2 files changed, 244 insertions(+), 82 deletions(-) diff --git a/src/protocols/rdp/keyboard.c b/src/protocols/rdp/keyboard.c index d2294237..1c1c9b60 100644 --- a/src/protocols/rdp/keyboard.c +++ b/src/protocols/rdp/keyboard.c @@ -199,10 +199,10 @@ static void guac_rdp_send_synchronize_event(guac_rdp_client* rdp_client, } /** - * Given a keyboard instance and X11 keysym, returns a pointer to the key - * structure that represents or can represent the key having that keysym within - * the keyboard, regardless of whether the key is currently defined. If no such - * key can exist (the keysym cannot be mapped or is out of range), NULL is + * Given a keyboard instance and X11 keysym, returns a pointer to the + * keys_by_keysym entry that represents the key having that keysym within the + * keyboard, regardless of whether the key is currently defined. If no such key + * can exist (the keysym cannot be mapped or is out of range), NULL is * returned. * * @param keyboard @@ -212,11 +212,11 @@ static void guac_rdp_send_synchronize_event(guac_rdp_client* rdp_client, * The keysym of the key to lookup within the given keyboard. * * @return - * A pointer to the guac_rdp_key structure which represents or can - * represent the key having the given keysym, or NULL if no such keysym can - * be defined within a guac_rdp_keyboard structure. + * A pointer to the keys_by_keysym entry which represents or can represent + * the key having the given keysym, or NULL if no such keysym can be + * defined within a guac_rdp_keyboard structure. */ -static guac_rdp_key* guac_rdp_keyboard_map_key(guac_rdp_keyboard* keyboard, +static guac_rdp_key** guac_rdp_keyboard_map_key(guac_rdp_keyboard* keyboard, int keysym) { int index; @@ -234,14 +234,79 @@ static guac_rdp_key* guac_rdp_keyboard_map_key(guac_rdp_keyboard* keyboard, return NULL; /* Corresponding key mapping (defined or not) has been located */ - return &(keyboard->keys[index]); + return &(keyboard->keys_by_keysym[index]); } /** - * Returns a pointer to the guac_rdp_key structure representing the definition - * and state of the key having the given keysym. If no such key is defined - * within the keyboard layout of the RDP server, NULL is returned. + * Returns the number of bits that are set within the given integer (the number + * of 1s in the binary expansion of the given integer). + * + * @param value + * The integer to read. + * + * @return + * The number of bits that are set within the given integer. + */ +static int guac_rdp_count_bits(unsigned int value) { + + int bits = 0; + + while (value) { + bits += value & 1; + value >>= 1; + } + + return bits; + +} + +/** + * Returns an estimated cost for sending the necessary RDP events to type the + * key described by the given guac_rdp_keysym_desc, given the current lock and + * modifier state of the keyboard. A higher cost value indicates that a greater + * number of events are expected to be required. + * + * Lower-cost approaches should be preferred when multiple alternatives exist + * for typing a particular key, as the lower cost implies fewer additional key + * events required to produce the expected behavior. For example, if Caps Lock + * is enabled, typing an uppercase "A" by pressing the "A" key has a lower cost + * than disabling Caps Lock and pressing Shift+A. + * + * @param keyboard + * The guac_rdp_keyboard associated with the current RDP session. + * + * @param def + * The guac_rdp_keysym_desc that describes the key being pressed, as well + * as any requirements that must be satisfied for the key to be interpreted + * as expected. + * + * @return + * An arbitrary integer value which indicates the overall estimated + * complexity of typing the given key. + */ +static int guac_rdp_keyboard_get_cost(guac_rdp_keyboard* keyboard, + const guac_rdp_keysym_desc* def) { + + /* Each change to any key requires one event, by definition */ + int cost = 1; + + /* Each change to a lock requires roughly two key events */ + unsigned int update_locks = (def->set_locks & ~keyboard->lock_flags) | (def->clear_locks & keyboard->lock_flags); + 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); + cost += guac_rdp_count_bits(update_modifiers); + + return cost; + +} + +/** + * Returns a pointer to the guac_rdp_key structure representing the + * definition(s) and state of the key having the given keysym. If no such key + * is defined within the keyboard layout of the RDP server, NULL is returned. * * @param keyboard * The guac_rdp_keyboard associated with the current RDP session. @@ -250,20 +315,120 @@ static guac_rdp_key* guac_rdp_keyboard_map_key(guac_rdp_keyboard* keyboard, * The keysym of the key to lookup within the given keyboard. * * @return - * A pointer to the guac_rdp_key structure representing the definition and - * state of the key having the given keysym, or NULL if no such key is + * A pointer to the guac_rdp_key structure representing the definition(s) + * and state of the key having the given keysym, or NULL if no such key is * defined within the keyboard layout of the RDP server. */ static guac_rdp_key* guac_rdp_keyboard_get_key(guac_rdp_keyboard* keyboard, int keysym) { /* Verify that the key is actually defined */ - guac_rdp_key* key = guac_rdp_keyboard_map_key(keyboard, keysym); - if (key == NULL || key->definition == NULL) + guac_rdp_key** key_by_keysym = guac_rdp_keyboard_map_key(keyboard, keysym); + if (key_by_keysym == NULL) return NULL; - /* Key is defined within keyboard */ - return key; + return *key_by_keysym; + +} + +/** + * Given a key which may have multiple possible definitions, returns the + * definition that currently has the lowest cost, taking into account the + * current keyboard lock and modifier states. + * + * @param keyboard + * The guac_rdp_keyboard associated with the current RDP session. + * + * @param key + * The key whose lowest-cost possible definition should be retrieved. + * + * @return + * A pointer to the guac_rdp_keysym_desc which defines the current + * lowest-cost method of typing the given key. + */ +static const guac_rdp_keysym_desc* guac_rdp_keyboard_get_definition(guac_rdp_keyboard* keyboard, + guac_rdp_key* key) { + + /* Consistently map the same entry so long as the key is held */ + if (key->pressed != NULL) + return key->pressed; + + /* Calculate cost of first definition of key (there must always be at least + * one definition) */ + const guac_rdp_keysym_desc* best_def = key->definitions[0]; + int best_cost = guac_rdp_keyboard_get_cost(keyboard, best_def); + + /* If further definitions exist, choose the definition with the lowest + * overall cost */ + for (int i = 1; i < key->num_definitions; i++) { + + const guac_rdp_keysym_desc* def = key->definitions[i]; + int cost = guac_rdp_keyboard_get_cost(keyboard, def); + + if (cost < best_cost) { + best_def = def; + best_cost = cost; + } + + } + + return best_def; + +} + +/** + * Adds the keysym/scancode mapping described by the given guac_rdp_keysym_desc + * to the internal mapping of the keyboard. If insufficient space remains for + * additional keysyms, or the given keysym has already reached the maximum + * number of possible definitions, the mapping is ignored and the failure is + * logged. + * + * @param keyboard + * The guac_rdp_keyboard associated with the current RDP session. + * + * @param mapping + * The keysym/scancode mapping that should be added to the given keyboard. + */ +static void guac_rdp_keyboard_add_mapping(guac_rdp_keyboard* keyboard, + const guac_rdp_keysym_desc* mapping) { + + /* Locate corresponding keysym-to-key translation entry within keyboard + * structure */ + guac_rdp_key** key_by_keysym = guac_rdp_keyboard_map_key(keyboard, mapping->keysym); + if (key_by_keysym == NULL) { + guac_client_log(keyboard->client, GUAC_LOG_DEBUG, "Ignoring unmappable keysym 0x%X", mapping->keysym); + return; + } + + /* If not yet pointing to a key, point keysym-to-key translation entry at + * next available storage */ + if (*key_by_keysym == NULL) { + + if (keyboard->num_keys == GUAC_RDP_KEYBOARD_MAX_KEYSYMS) { + guac_client_log(keyboard->client, GUAC_LOG_DEBUG, "Key definition " + "for keysym 0x%X dropped: Keymap exceeds maximum " + "supported number of keysyms", + mapping->keysym); + return; + } + + *key_by_keysym = &keyboard->keys[keyboard->num_keys++]; + + } + + guac_rdp_key* key = *key_by_keysym; + + /* Add new definition only if sufficient space remains */ + if (key->num_definitions == GUAC_RDP_KEY_MAX_DEFINITIONS) { + guac_client_log(keyboard->client, GUAC_LOG_DEBUG, "Key definition " + "for keysym 0x%X dropped: Maximum number of possible " + "definitions has been reached for this keysym", + mapping->keysym); + return; + } + + /* Store new possible definition of key */ + key->definitions[key->num_definitions++] = mapping; } @@ -281,37 +446,21 @@ static guac_rdp_key* guac_rdp_keyboard_get_key(guac_rdp_keyboard* keyboard, * The keymap to use to populate the given client's keysym/scancode * mapping. */ -static void __guac_rdp_keyboard_load_keymap(guac_rdp_keyboard* keyboard, +static void guac_rdp_keyboard_load_keymap(guac_rdp_keyboard* keyboard, const guac_rdp_keymap* keymap) { - /* Get mapping */ - const guac_rdp_keysym_desc* mapping = keymap->mapping; - /* If parent exists, load parent first */ if (keymap->parent != NULL) - __guac_rdp_keyboard_load_keymap(keyboard, keymap->parent); + guac_rdp_keyboard_load_keymap(keyboard, keymap->parent); /* Log load */ guac_client_log(keyboard->client, GUAC_LOG_INFO, "Loading keymap \"%s\"", keymap->name); - /* Load mapping into keymap */ + /* Copy mapping into keymap */ + const guac_rdp_keysym_desc* mapping = keymap->mapping; while (mapping->keysym != 0) { - - /* Locate corresponding key definition within keyboard */ - guac_rdp_key* key = guac_rdp_keyboard_map_key(keyboard, - mapping->keysym); - - /* Copy mapping (if key is mappable) */ - if (key != NULL) - key->definition = mapping; - else - guac_client_log(keyboard->client, GUAC_LOG_DEBUG, - "Ignoring unmappable keysym 0x%X", mapping->keysym); - - /* Next keysym */ - mapping++; - + guac_rdp_keyboard_add_mapping(keyboard, mapping++); } } @@ -323,7 +472,7 @@ guac_rdp_keyboard* guac_rdp_keyboard_alloc(guac_client* client, keyboard->client = client; /* Load keymap into keyboard */ - __guac_rdp_keyboard_load_keymap(keyboard, keymap); + guac_rdp_keyboard_load_keymap(keyboard, keymap); return keyboard; @@ -358,16 +507,15 @@ int guac_rdp_keyboard_is_defined(guac_rdp_keyboard* keyboard, int keysym) { * Zero if the key was successfully pressed/released, non-zero if the key * cannot be sent using RDP key events. */ -static int guac_rdp_keyboard_send_defined_key(guac_rdp_keyboard* keyboard, +static const guac_rdp_keysym_desc* guac_rdp_keyboard_send_defined_key(guac_rdp_keyboard* keyboard, guac_rdp_key* key, int pressed) { guac_client* client = keyboard->client; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; - if (key->definition->scancode == 0) - return 1; - - const guac_rdp_keysym_desc* keysym_desc = key->definition; + const guac_rdp_keysym_desc* keysym_desc = guac_rdp_keyboard_get_definition(keyboard, key); + if (keysym_desc->scancode == 0) + return NULL; /* Update state of required locks and modifiers only when key is just * now being pressed */ @@ -385,7 +533,7 @@ static int guac_rdp_keyboard_send_defined_key(guac_rdp_keyboard* keyboard, guac_rdp_send_key_event(rdp_client, keysym_desc->scancode, keysym_desc->flags, pressed); - return 0; + return keysym_desc; } @@ -491,17 +639,12 @@ int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, } - /* If key is known, update stored state, ignoring the key event entirely if - * state is not actually changing */ + /* 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); if (key != NULL) { - - guac_rdp_key_state new_state = pressed ? GUAC_RDP_KEY_PRESSED : GUAC_RDP_KEY_RELEASED; - if (key->state == new_state) + if ((!pressed && key->pressed == NULL) || (pressed && key->pressed != NULL)) return 0; - - key->state = new_state; - } /* Toggle locks and set modifiers on keydown */ @@ -515,15 +658,20 @@ int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, keyboard->modifier_flags &= ~guac_rdp_keyboard_modifier_flag(keysym); } - /* Attempt to send using normal RDP key events */ - if (key == NULL && !guac_rdp_keyboard_send_defined_key(keyboard, key, pressed)) - return 0; + /* 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 (pressed) + if (definition == NULL && pressed) { guac_rdp_keyboard_send_missing_key(keyboard, keysym); + } return 0; diff --git a/src/protocols/rdp/keyboard.h b/src/protocols/rdp/keyboard.h index e8b28a62..94888160 100644 --- a/src/protocols/rdp/keyboard.h +++ b/src/protocols/rdp/keyboard.h @@ -25,22 +25,17 @@ #include /** - * The current local state of a key - either pressed or released. + * The maximum number of distinct keysyms that any particular keyboard may support. */ -typedef enum guac_rdp_key_state { +#define GUAC_RDP_KEYBOARD_MAX_KEYSYMS 1024 - /** - * The state associated with a key that is released (not currently - * pressed / held down). - */ - GUAC_RDP_KEY_RELEASED = 0, - - /** - * The state associated with a key that is currently pressed (held down). - */ - GUAC_RDP_KEY_PRESSED = 1 - -} guac_rdp_key_state; +/** + * The maximum number of unique modifier variations that any particular keysym + * may define. For example, on a US English keyboard, an uppercase "A" may be + * typed by pressing Shift+A with Caps Lock unset, or by pressing A with Caps + * Lock set (two variations). + */ +#define GUAC_RDP_KEY_MAX_DEFINITIONS 4 /** * A representation of a single key within the overall local keyboard, @@ -50,21 +45,25 @@ typedef enum guac_rdp_key_state { typedef struct guac_rdp_key { /** - * The definition of this key within the RDP server's keymap (keyboard - * layout). This definition describes which scancode corresponds to this + * All definitions of this key within the RDP server's keymap (keyboard + * layout). Each definition describes which scancode corresponds to this * key from the perspective of the RDP server, as well as which other * scancodes must be pressed/released for this key to have the desired - * meaning. If this key does not exist within the RDP server's keymap, this - * will be NULL. + * meaning. */ - const guac_rdp_keysym_desc* definition; + const guac_rdp_keysym_desc* definitions[GUAC_RDP_KEY_MAX_DEFINITIONS]; /** - * The local state of this key. For the sake of simplicity, it is assumed - * that this state is also an accurate representation of the remote state - * of this key within the RDP session. + * The number of definitions within the definitions array. If this key does + * not exist within the RDP server's keymap, this will be 0. */ - guac_rdp_key_state state; + int num_definitions; + + /** + * The definition of this key that is currently pressed. If this key is not + * currently pressed, this will be NULL. + */ + const guac_rdp_keysym_desc* pressed; } guac_rdp_key; @@ -101,6 +100,11 @@ typedef struct guac_rdp_keyboard { */ int synchronized; + /** + * The number of keys stored within the keys array. + */ + unsigned int num_keys; + /** * The local state of all keys, as well as the necessary information to * translate received keysyms into scancodes or sequences of scancodes for @@ -108,13 +112,23 @@ typedef struct guac_rdp_keyboard { * events, while the information describing the behavior and scancode * mapping of each key is populated based on an associated keymap. * + * Keys within this array are in arbitrary order. + */ + guac_rdp_key keys[GUAC_RDP_KEYBOARD_MAX_KEYSYMS]; + + /** + * Lookup table into the overall keys array, locating the guac_rdp_key + * associated with any particular keysym. If a keysym has no corresponding + * guac_rdp_key within the keys array, its entry within this lookuptable + * will be NULL. + * * The index of the key for a given keysym is determined based on a * simple transformation of the keysym itself. Keysyms between 0x0000 and * 0xFFFF inclusive are mapped to 0x00000 through 0x0FFFF, while keysyms * between 0x1000000 and 0x100FFFF inclusive (keysyms which are derived * from Unicode) are mapped to 0x10000 through 0x1FFFF. */ - guac_rdp_key keys[0x20000]; + guac_rdp_key* keys_by_keysym[0x20000]; } guac_rdp_keyboard; From 48b3d5038fef1f6aed15a7ebb1f91c1db0a7fce3 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 15:14:27 -0700 Subject: [PATCH 06/22] GUACAMOLE-518: Automatically release any automatically-pressed keys after user has released all keys on the client side. --- src/protocols/rdp/decompose.c | 8 +++---- src/protocols/rdp/input.c | 2 +- src/protocols/rdp/keyboard.c | 44 ++++++++++++++++++++++++++++++----- src/protocols/rdp/keyboard.h | 41 +++++++++++++++++++++++++++++++- 4 files changed, 83 insertions(+), 12 deletions(-) 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 From 024e281252be0ef3bca4495ba44fe13d5b8ecf79 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 20:33:12 -0700 Subject: [PATCH 07/22] GUACAMOLE-518: Update US English keymap to define behavior of Caps Lock. On US English keyboards, Caps Lock affects only letters and is undone by Shift. --- src/protocols/rdp/keymaps/en_us_qwerty.keymap | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/protocols/rdp/keymaps/en_us_qwerty.keymap b/src/protocols/rdp/keymaps/en_us_qwerty.keymap index 4df9e2e9..ddb38e15 100644 --- a/src/protocols/rdp/keymaps/en_us_qwerty.keymap +++ b/src/protocols/rdp/keymaps/en_us_qwerty.keymap @@ -21,13 +21,23 @@ parent "base" name "en-us-qwerty" freerdp "KBD_US" -map -shift 0x29 0x02..0x0D ~ "`1234567890-=" -map -shift 0x10..0x1B 0x2B ~ "qwertyuiop[]\" -map -shift 0x1E..0x28 ~ "asdfghjkl;'" -map -shift 0x2C..0x35 ~ "zxcvbnm,./" +map -caps -shift 0x29 0x02..0x0D ~ "`1234567890-=" +map -caps -shift 0x10..0x1B 0x2B ~ "qwertyuiop[]\" +map -caps -shift 0x1E..0x28 ~ "asdfghjkl;'" +map -caps -shift 0x2C..0x35 ~ "zxcvbnm,./" -map +shift 0x29 0x02..0x0D ~ "~!@#$%^&*()_+" -map +shift 0x10..0x1B 0x2B ~ "QWERTYUIOP{}|" -map +shift 0x1E..0x28 ~ "ASDFGHJKL:"" -map +shift 0x2C..0x35 ~ "ZXCVBNM<>?" +map -caps +shift 0x29 0x02..0x0D ~ "~!@#$%^&*()_+" +map -caps +shift 0x10..0x1B 0x2B ~ "QWERTYUIOP{}|" +map -caps +shift 0x1E..0x28 ~ "ASDFGHJKL:"" +map -caps +shift 0x2C..0x35 ~ "ZXCVBNM<>?" + +map +caps -shift 0x29 0x02..0x0D ~ "`1234567890-=" +map +caps -shift 0x10..0x1B 0x2B ~ "QWERTYUIOP[]\" +map +caps -shift 0x1E..0x28 ~ "ASDFGHJKL;'" +map +caps -shift 0x2C..0x35 ~ "ZXCVBNM,./" + +map +caps +shift 0x29 0x02..0x0D ~ "~!@#$%^&*()_+" +map +caps +shift 0x10..0x1B 0x2B ~ "qwertyuiop{}|" +map +caps +shift 0x1E..0x28 ~ "asdfghjkl:"" +map +caps +shift 0x2C..0x35 ~ "zxcvbnm<>?" From 31a415cc595739f9fa4be4c02d3e1804af46f2f5 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 20:34:04 -0700 Subject: [PATCH 08/22] GUACAMOLE-518: Update UK English keymap to define behavior of Caps Lock. On UK English keyboards, Caps Lock affects only letters (including accented letters) and is undone by Shift. --- src/protocols/rdp/keymaps/en_gb_qwerty.keymap | 66 +++++++++++++------ 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/src/protocols/rdp/keymaps/en_gb_qwerty.keymap b/src/protocols/rdp/keymaps/en_gb_qwerty.keymap index 92fe9def..9f338983 100644 --- a/src/protocols/rdp/keymaps/en_gb_qwerty.keymap +++ b/src/protocols/rdp/keymaps/en_gb_qwerty.keymap @@ -25,32 +25,56 @@ freerdp "KBD_UNITED_KINGDOM" # Basic keys # -map -altgr -shift 0x29 0x02..0x0D ~ "`1234567890-=" -map -altgr -shift 0x10..0x1B ~ "qwertyuiop[]" -map -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjkl;'#" -map -altgr -shift 0x56 0x2C..0x35 ~ "\zxcvbnm,./" +map -caps -altgr -shift 0x29 0x02..0x0D ~ "`1234567890-=" +map -caps -altgr -shift 0x10..0x1B ~ "qwertyuiop[]" +map -caps -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjkl;'#" +map -caps -altgr -shift 0x56 0x2C..0x35 ~ "\zxcvbnm,./" -map -altgr +shift 0x29 0x02..0x0D ~ "¬!"£$%^&*()_+" -map -altgr +shift 0x10..0x1B ~ "QWERTYUIOP{}" -map -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKL:@~" -map -altgr +shift 0x56 0x2C..0x35 ~ "|ZXCVBNM<>?" +map -caps -altgr +shift 0x29 0x02..0x0D ~ "¬!"£$%^&*()_+" +map -caps -altgr +shift 0x10..0x1B ~ "QWERTYUIOP{}" +map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKL:@~" +map -caps -altgr +shift 0x56 0x2C..0x35 ~ "|ZXCVBNM<>?" + +map +caps -altgr -shift 0x29 0x02..0x0D ~ "`1234567890-=" +map +caps -altgr -shift 0x10..0x1B ~ "QWERTYUIOP[]" +map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKL;'#" +map +caps -altgr -shift 0x56 0x2C..0x35 ~ "\ZXCVBNM,./" + +map +caps -altgr +shift 0x29 0x02..0x0D ~ "¬!"£$%^&*()_+" +map +caps -altgr +shift 0x10..0x1B ~ "qwertyuiop{}" +map +caps -altgr +shift 0x1E..0x28 0x2B ~ "asdfghjkl:@~" +map +caps -altgr +shift 0x56 0x2C..0x35 ~ "|zxcvbnm<>?" # -# Keys requiring AltGr +# Keys requiring AltGr (some of which are affected by Caps Lock) # map +altgr -shift 0x29 ~ "¦" map +altgr -shift 0x05 ~ "€" -map +altgr -shift 0x12 ~ "é" -map +altgr +shift 0x12 ~ "É" -map +altgr -shift 0x16 ~ "ú" -map +altgr +shift 0x16 ~ "Ú" -map +altgr -shift 0x17 ~ "í" -map +altgr +shift 0x17 ~ "Í" -map +altgr -shift 0x18 ~ "ó" -map +altgr +shift 0x18 ~ "Ó" -map +altgr -shift 0x1E ~ "á" -map +altgr +shift 0x1E ~ "Á" -map +altgr -shift 0x2E ~ "ç" -map +altgr +shift 0x2E ~ "Ç" + +map -caps +altgr -shift 0x12 ~ "é" +map -caps +altgr +shift 0x12 ~ "É" +map -caps +altgr -shift 0x16 ~ "ú" +map -caps +altgr +shift 0x16 ~ "Ú" +map -caps +altgr -shift 0x17 ~ "í" +map -caps +altgr +shift 0x17 ~ "Í" +map -caps +altgr -shift 0x18 ~ "ó" +map -caps +altgr +shift 0x18 ~ "Ó" +map -caps +altgr -shift 0x1E ~ "á" +map -caps +altgr +shift 0x1E ~ "Á" +map -caps +altgr -shift 0x2E ~ "ç" +map -caps +altgr +shift 0x2E ~ "Ç" + +map +caps +altgr +shift 0x12 ~ "é" +map +caps +altgr -shift 0x12 ~ "É" +map +caps +altgr +shift 0x16 ~ "ú" +map +caps +altgr -shift 0x16 ~ "Ú" +map +caps +altgr +shift 0x17 ~ "í" +map +caps +altgr -shift 0x17 ~ "Í" +map +caps +altgr +shift 0x18 ~ "ó" +map +caps +altgr -shift 0x18 ~ "Ó" +map +caps +altgr +shift 0x1E ~ "á" +map +caps +altgr -shift 0x1E ~ "Á" +map +caps +altgr +shift 0x2E ~ "ç" +map +caps +altgr -shift 0x2E ~ "Ç" From e9652becfd1a7123af32708d67f3b694eb154a1e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 20:34:47 -0700 Subject: [PATCH 09/22] GUACAMOLE-518: Update Hungarian keymap to define behavior of Caps Lock. On Hungarian keyboards, Caps Lock affects only letters (including accented letters) except those requiring AltGr, and is undone by Shift. --- src/protocols/rdp/keymaps/hu_hu_qwertz.keymap | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/protocols/rdp/keymaps/hu_hu_qwertz.keymap b/src/protocols/rdp/keymaps/hu_hu_qwertz.keymap index bcb6a67c..54f1d756 100644 --- a/src/protocols/rdp/keymaps/hu_hu_qwertz.keymap +++ b/src/protocols/rdp/keymaps/hu_hu_qwertz.keymap @@ -25,16 +25,25 @@ freerdp "KBD_HUNGARIAN" # Basic keys # -map -altgr -shift 0x29 0x02..0x0D ~ "0123456789öüó" -map -altgr -shift 0x10..0x1B ~ "qwertzuıopőú" -map -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjkléáű" -map -altgr -shift 0x56 0x2C..0x35 ~ "íyxcvbnm,.-" +map -caps -altgr -shift 0x29 0x02..0x0D ~ "0123456789öüó" +map -caps -altgr -shift 0x10..0x1B ~ "qwertzuıopőú" +map -caps -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjkléáű" +map -caps -altgr -shift 0x56 0x2C..0x35 ~ "íyxcvbnm,.-" -map -altgr +shift 0x29 0x02..0x0D ~ "§'"+!%/=()ÖÜÓ" -map -altgr +shift 0x10..0x1B ~ "QWERTZUIOPŐÚ" -map -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÉÁŰ" -map -altgr +shift 0x56 0x2C..0x35 ~ "ÍYXCVBNM?:_" +map -caps -altgr +shift 0x29 0x02..0x0D ~ "§'"+!%/=()ÖÜÓ" +map -caps -altgr +shift 0x10..0x1B ~ "QWERTZUIOPŐÚ" +map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÉÁŰ" +map -caps -altgr +shift 0x56 0x2C..0x35 ~ "ÍYXCVBNM?:_" +map +caps -altgr -shift 0x29 0x02..0x0D ~ "0123456789ÖÜÓ" +map +caps -altgr -shift 0x10..0x1B ~ "QWERTZUIOPŐÚ" +map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÉÁŰ" +map +caps -altgr -shift 0x56 0x2C..0x35 ~ "ÍYXCVBNM,.-" + +map +caps -altgr +shift 0x29 0x02..0x0D ~ "§'"+!%/=()öüó" +map +caps -altgr +shift 0x10..0x1B ~ "qwertzuiopőú" +map +caps -altgr +shift 0x1E..0x28 0x2B ~ "asdfghjkléáű" +map +caps -altgr +shift 0x56 0x2C..0x35 ~ "íyxcvbnm?:_" # # Keys requiring AltGr From 2cec040b9e0b1aaeabdfcf0381289a82c002ccb4 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 20:35:49 -0700 Subject: [PATCH 10/22] GUACAMOLE-518: Update Danish keymap to define behavior of Caps Lock. On Danish keyboards, Caps Lock affects only letters (including accented letters) and is undone by Shift. --- src/protocols/rdp/keymaps/da_dk_qwerty.keymap | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/protocols/rdp/keymaps/da_dk_qwerty.keymap b/src/protocols/rdp/keymaps/da_dk_qwerty.keymap index c4893d1b..aa6139cf 100644 --- a/src/protocols/rdp/keymaps/da_dk_qwerty.keymap +++ b/src/protocols/rdp/keymaps/da_dk_qwerty.keymap @@ -25,15 +25,25 @@ freerdp "KBD_DANISH" # Basic keys # -map -altgr -shift 0x29 0x02..0x0C ~ "§1234567890+" -map -altgr -shift 0x10..0x1A ~ "qwertyuiopå" -map -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklæø'" -map -altgr -shift 0x56 0x2C..0x35 ~ "ZXCVBNM;:_" +map -caps -altgr +shift 0x29 0x02..0x0C ~ "½!"#¤%&/()=?" +map -caps -altgr +shift 0x10..0x1A ~ "QWERTYUIOPÅ" +map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÆØ*" +map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">ZXCVBNM;:_" + +map +caps -altgr -shift 0x29 0x02..0x0C ~ "§1234567890+" +map +caps -altgr -shift 0x10..0x1A ~ "QWERTYUIOPÅ" +map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÆØ'" +map +caps -altgr -shift 0x56 0x2C..0x35 ~ "zxcvbnm;:_" # # Keys requiring AltGr From ba3d1de3bbf80488732b9ecaf4fe40ba928675cd Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 20:37:49 -0700 Subject: [PATCH 11/22] GUACAMOLE-518: Update Spanish keymap to define behavior of Caps Lock. On Spanish keyboards, Caps Lock affects only letters (including accented letters) and is undone by Shift. --- src/protocols/rdp/keymaps/es_es_qwerty.keymap | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/protocols/rdp/keymaps/es_es_qwerty.keymap b/src/protocols/rdp/keymaps/es_es_qwerty.keymap index bd0f2814..93453cff 100644 --- a/src/protocols/rdp/keymaps/es_es_qwerty.keymap +++ b/src/protocols/rdp/keymaps/es_es_qwerty.keymap @@ -25,15 +25,25 @@ freerdp "KBD_SPANISH" # Basic keys # -map -altgr -shift 0x29 0x02..0x0D ~ "º1234567890'¡" -map -altgr -shift 0x10..0x19 0x1B ~ "qwertyuiop+" -map -altgr -shift 0x1E..0x27 0x2B ~ "asdfghjklñç" -map -altgr -shift 0x56 0x2C..0x35 ~ "ZXCVBNM;:_" +map -caps -altgr +shift 0x29 0x02..0x0D ~ "ª!"·$%&/()=?¿" +map -caps -altgr +shift 0x10..0x19 0x1B ~ "QWERTYUIOP*" +map -caps -altgr +shift 0x1E..0x27 0x2B ~ "ASDFGHJKLÑÇ" +map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">ZXCVBNM;:_" + +map +caps -altgr -shift 0x29 0x02..0x0D ~ "º1234567890'¡" +map +caps -altgr -shift 0x10..0x19 0x1B ~ "QWERTYUIOP+" +map +caps -altgr -shift 0x1E..0x27 0x2B ~ "ASDFGHJKLÑÇ" +map +caps -altgr -shift 0x56 0x2C..0x35 ~ "zxcvbnm;:_" # # Keys requiring AltGr From 5c1a2fc44c3b76d4ba1115d808662e9f8413c8dd Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 20:38:56 -0700 Subject: [PATCH 12/22] GUACAMOLE-518: Update Latin American keymap to define behavior of Caps Lock. On Latin American keyboards, Caps Lock affects only letters (including accented letters) and is undone by Shift. --- .../rdp/keymaps/es_latam_qwerty.keymap | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/protocols/rdp/keymaps/es_latam_qwerty.keymap b/src/protocols/rdp/keymaps/es_latam_qwerty.keymap index 662f4b8b..4c4ee578 100644 --- a/src/protocols/rdp/keymaps/es_latam_qwerty.keymap +++ b/src/protocols/rdp/keymaps/es_latam_qwerty.keymap @@ -25,15 +25,25 @@ freerdp "KBD_LATIN_AMERICAN" # Basic keys # -map -altgr -shift 0x29 0x02..0x0D ~ "|1234567890'¿" -map -altgr -shift 0x10..0x19 0x1B ~ "qwertyuiop+" -map -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklñ{}" -map -altgr -shift 0x56 0x2C..0x35 ~ "ZXCVBNM;:_" +map -caps -altgr +shift 0x29 0x02..0x0D ~ "º!"#$%&/()=?¡" +map -caps -altgr +shift 0x10..0x19 0x1B ~ "QWERTYUIOP*" +map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÑ[]" +map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">ZXCVBNM;:_" + +map +caps -altgr -shift 0x29 0x02..0x0D ~ "|1234567890'¿" +map +caps -altgr -shift 0x10..0x19 0x1B ~ "QWERTYUIOP+" +map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÑ{}" +map +caps -altgr -shift 0x56 0x2C..0x35 ~ "zxcvbnm;:_" # # Keys requiring AltGr From 38737a83531bc523d65ae718170c82625d361efb Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 20:42:31 -0700 Subject: [PATCH 13/22] GUACAMOLE-518: Update Brazilian Portuguese keymap to define behavior of Caps Lock. On Brazilian Portuguese keyboards, Caps Lock affects only letters (including accented letters) and is undone by Shift. --- src/protocols/rdp/keymaps/pt_br_qwerty.keymap | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/protocols/rdp/keymaps/pt_br_qwerty.keymap b/src/protocols/rdp/keymaps/pt_br_qwerty.keymap index 54df313c..9bb2bb0f 100644 --- a/src/protocols/rdp/keymaps/pt_br_qwerty.keymap +++ b/src/protocols/rdp/keymaps/pt_br_qwerty.keymap @@ -25,15 +25,25 @@ freerdp "KBD_PORTUGUESE_BRAZILIAN_ABNT2" # Basic keys # -map -altgr -shift 0x29 0x02..0x0D ~ "'1234567890-=" -map -altgr -shift 0x10..0x19 0x1B ~ "qwertyuiop[" -map -altgr -shift 0x1E..0x27 0x2B ~ "asdfghjklç]" -map -altgr -shift 0x56 0x2C..0x36 ~ "\zxcvbnm,.;/" +map -caps -altgr -shift 0x29 0x02..0x0D ~ "'1234567890-=" +map -caps -altgr -shift 0x10..0x19 0x1B ~ "qwertyuiop[" +map -caps -altgr -shift 0x1E..0x27 0x2B ~ "asdfghjklç]" +map -caps -altgr -shift 0x56 0x2C..0x36 ~ "\zxcvbnm,.;/" -map -altgr +shift 0x29 0x02..0x06 0x08..0x0D ~ ""!@#$%&*()_+" -map -altgr +shift 0x10..0x19 0x1B ~ "QWERTYUIOP{" -map -altgr +shift 0x1E..0x27 0x2B ~ "ASDFGHJKLÇ}" -map -altgr +shift 0x56 0x2C..0x36 ~ "|ZXCVBNM<>:?" +map -caps -altgr +shift 0x29 0x02..0x06 0x08..0x0D ~ ""!@#$%&*()_+" +map -caps -altgr +shift 0x10..0x19 0x1B ~ "QWERTYUIOP{" +map -caps -altgr +shift 0x1E..0x27 0x2B ~ "ASDFGHJKLÇ}" +map -caps -altgr +shift 0x56 0x2C..0x36 ~ "|ZXCVBNM<>:?" + +map +caps -altgr -shift 0x29 0x02..0x0D ~ "'1234567890-=" +map +caps -altgr -shift 0x10..0x19 0x1B ~ "QWERTYUIOP[" +map +caps -altgr -shift 0x1E..0x27 0x2B ~ "ASDFGHJKLÇ]" +map +caps -altgr -shift 0x56 0x2C..0x36 ~ "\ZXCVBNM,.;/" + +map +caps -altgr +shift 0x29 0x02..0x06 0x08..0x0D ~ ""!@#$%&*()_+" +map +caps -altgr +shift 0x10..0x19 0x1B ~ "qwertyuiop{" +map +caps -altgr +shift 0x1E..0x27 0x2B ~ "asdfghjklç}" +map +caps -altgr +shift 0x56 0x2C..0x36 ~ "|zxcvbnm<>:?" # # Keys requiring AltGr From b69248048cc825d118aeb750fcedd797871d942b Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 20:44:12 -0700 Subject: [PATCH 14/22] GUACAMOLE-518: Update Swedish keymap to define behavior of Caps Lock. On Swedish keyboards, Caps Lock affects only letters (including accented letters) and is undone by Shift. --- src/protocols/rdp/keymaps/sv_se_qwerty.keymap | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/protocols/rdp/keymaps/sv_se_qwerty.keymap b/src/protocols/rdp/keymaps/sv_se_qwerty.keymap index 0dcc7f4b..6ab8b7a5 100644 --- a/src/protocols/rdp/keymaps/sv_se_qwerty.keymap +++ b/src/protocols/rdp/keymaps/sv_se_qwerty.keymap @@ -25,15 +25,25 @@ freerdp "KBD_SWEDISH" # Basic keys # -map -altgr -shift 0x29 0x02..0x0C ~ "§1234567890+" -map -altgr -shift 0x10..0x1A ~ "qwertyuiopå" -map -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklöä'" -map -altgr -shift 0x56 0x2C..0x35 ~ "ZXCVBNM;:_" +map -caps -altgr +shift 0x29 0x02..0x0C ~ "½!"#¤%&/()=?" +map -caps -altgr +shift 0x10..0x1A ~ "QWERTYUIOPÅ" +map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÖÄ*" +map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">ZXCVBNM;:_" + +map +caps -altgr -shift 0x29 0x02..0x0C ~ "§1234567890+" +map +caps -altgr -shift 0x10..0x1A ~ "QWERTYUIOPÅ" +map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÖÄ'" +map +caps -altgr -shift 0x56 0x2C..0x35 ~ "zxcvbnm;:_" # # Keys requiring AltGr From 1117cf052cd88baa1cfbc59ad26814db08840ff7 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 20:55:53 -0700 Subject: [PATCH 15/22] GUACAMOLE-518: Update Turkish-Q keymap to define behavior of Caps Lock. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Turkish-Q keyboards, Caps Lock affects only letters (including accented letters) except for "é", and is undone by Shift. --- src/protocols/rdp/keymaps/tr_tr_qwerty.keymap | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/protocols/rdp/keymaps/tr_tr_qwerty.keymap b/src/protocols/rdp/keymaps/tr_tr_qwerty.keymap index ca5e2360..d46a0fda 100644 --- a/src/protocols/rdp/keymaps/tr_tr_qwerty.keymap +++ b/src/protocols/rdp/keymaps/tr_tr_qwerty.keymap @@ -25,17 +25,27 @@ freerdp "KBD_TURKISH_Q" # Basic keys # -map -altgr -shift 0x29 0x02..0x0D ~ ""1234567890*-" -map -altgr -shift 0x10..0x1B ~ "qwertyuıopğü" -map -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklşi," -map -altgr -shift 0x56 0x2C..0x35 ~ "ZXCVBNMÖÇ:" +map -caps -altgr +shift 0x29 0x02..0x03 ~ "é!'" +map -caps -altgr +shift 0x05..0x0D ~ "+%&/()=?_" +map -caps -altgr +shift 0x10..0x1B ~ "QWERTYUIOPĞÜ" +map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLŞİ;" +map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">ZXCVBNMÖÇ:" +map +caps -altgr -shift 0x29 0x02..0x0D ~ ""1234567890*-" +map +caps -altgr -shift 0x10..0x1B ~ "QWERTYUIOPĞÜ" +map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLŞİ," +map +caps -altgr -shift 0x56 0x2C..0x35 ~ "zxcvbnmöç:" # # Keys requiring AltGr @@ -47,21 +57,26 @@ map +altgr -shift 0x08..0x0D ~ "{[]}\|" map +altgr -shift 0x10 ~ "@" map +altgr -shift 0x12 ~ "€" map +altgr -shift 0x14 ~ "₺" -map +altgr -shift 0x17 ~ "i" - -map +altgr -shift 0x1E ~ "æ" map +altgr -shift 0x1F ~ "ß" map +altgr -shift 0x56 ~ "|" # -# Keys requiring AltGr & Shift +# Keys requiring AltGr and vary by Shift and Caps Lock # -map +altgr +shift 0x17 ~ "İ" +map -caps +altgr -shift 0x17 ~ "i" +map -caps +altgr -shift 0x1E ~ "æ" -map +altgr +shift 0x1E ~ "Æ" +map -caps +altgr +shift 0x17 ~ "İ" +map -caps +altgr +shift 0x1E ~ "Æ" + +map +caps +altgr -shift 0x17 ~ "İ" +map +caps +altgr -shift 0x1E ~ "Æ" + +map +caps +altgr +shift 0x1E ~ "æ" +map +caps +altgr +shift 0x17 ~ "i" # # Dead keys From f884ab76b138b363ff55a6e1cd9785a335f1cb8d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 20:58:51 -0700 Subject: [PATCH 16/22] GUACAMOLE-518: Update Italian keymap to define behavior of Caps Lock. On Italian keyboards, Caps Lock affects only non-accented letters and is undone by Shift. --- src/protocols/rdp/keymaps/it_it_qwerty.keymap | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/protocols/rdp/keymaps/it_it_qwerty.keymap b/src/protocols/rdp/keymaps/it_it_qwerty.keymap index 6e9d9dbc..54f2172a 100644 --- a/src/protocols/rdp/keymaps/it_it_qwerty.keymap +++ b/src/protocols/rdp/keymaps/it_it_qwerty.keymap @@ -25,15 +25,25 @@ freerdp "KBD_ITALIAN" # Basic keys # -map -altgr -shift 0x29 0x02..0x0D ~ "\1234567890'ì" -map -altgr -shift 0x10..0x1B ~ "qwertyuiopè+" -map -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklòàù" -map -altgr -shift 0x56 0x2C..0x35 ~ "ZXCVBNM;:_" +map -caps -altgr +shift 0x29 0x02..0x0D ~ "|!"£$%&/()=?^" +map -caps -altgr +shift 0x10..0x1B ~ "QWERTYUIOPé*" +map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLç°§" +map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">ZXCVBNM;:_" + +map +caps -altgr -shift 0x29 0x02..0x0D ~ "\1234567890'ì" +map +caps -altgr -shift 0x10..0x1B ~ "QWERTYUIOPè+" +map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLòàù" +map +caps -altgr -shift 0x56 0x2C..0x35 ~ "zxcvbnm;:_" # # Keys requiring AltGr From 31d05de72a4c0b29c5ab19ad496819bc3e42cad8 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 21:26:32 -0700 Subject: [PATCH 17/22] GUACAMOLE-518: Update Belgian French keymap to define behavior of Caps Lock. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Belgian French keyboards, Caps Lock behaves like Shift, affects all characters including dead keys but excluding "<", ">", "²", "³", and any keys requiring AltGr, and is undone by Shift. --- src/protocols/rdp/keymaps/fr_be_azerty.keymap | 49 +++++++++++++------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/src/protocols/rdp/keymaps/fr_be_azerty.keymap b/src/protocols/rdp/keymaps/fr_be_azerty.keymap index cf2def61..c9f9add1 100644 --- a/src/protocols/rdp/keymaps/fr_be_azerty.keymap +++ b/src/protocols/rdp/keymaps/fr_be_azerty.keymap @@ -25,18 +25,28 @@ freerdp "KBD_BELGIAN_FRENCH" # Basic keys # -map -altgr -shift 0x29 0x02..0x0D ~ "²&é"'(§è!çà)_" -map -altgr -shift 0x10..0x19 0x1B ~ "azertyuiop$" -map -altgr -shift 0x1E..0x28 0x2B ~ "qsdfghjklmùµ" -map -altgr -shift 0x56 0x2C..0x35 ~ "WXCVBN?./+" +map -caps -altgr +shift 0x29 0x02..0x0D ~ "³1234567890°-" +map -caps -altgr +shift 0x10..0x19 0x1B ~ "AZERTYUIOP£" +map -caps -altgr +shift 0x1E..0x28 0x2B ~ "QSDFGHJKLM%£" +map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">WXCVBN?./+" + +map +caps -altgr -shift 0x29 0x02..0x0D ~ "²1234567890°-" +map +caps -altgr -shift 0x10..0x19 0x1B ~ "AZERTYUIOP£" +map +caps -altgr -shift 0x1E..0x28 0x2B ~ "QSDFGHJKLM%£" +map +caps -altgr -shift 0x56 0x2C..0x35 ~ "wxcvbn,;:=" # -# Keys requiring AltGr +# Keys requiring AltGr (unaffected by Caps Lock, but Shift must not be pressed) # map +altgr -shift 0x02..0x04 ~ "|@#" @@ -48,11 +58,20 @@ map +altgr -shift 0x56 ~ "\" map +altgr -shift 0x07 ~ "^" # -# Dead keys +# Dead keys requiring AltGr (unaffected by Caps Lock or Shift) # -map -altgr -shift 0x1A ~ 0xFE52 # Dead circumflex -map -altgr +shift 0x1A ~ 0xFE57 # Dead umlaut -map +altgr -shift 0x35 ~ 0xFE53 # Dead tilde -map +altgr -shift 0x28 ~ 0xFE51 # Dead acute -map +altgr -shift 0x2B ~ 0xFE50 # Dead grave +map +altgr 0x35 ~ 0xFE53 # Dead tilde +map +altgr 0x28 ~ 0xFE51 # Dead acute +map +altgr 0x2B ~ 0xFE50 # Dead grave + +# +# Dead keys (affected by Caps Lock and Shift) +# + +map -caps -altgr -shift 0x1A ~ 0xFE52 # Dead circumflex +map -caps -altgr +shift 0x1A ~ 0xFE57 # Dead umlaut + +map +caps -altgr -shift 0x1A ~ 0xFE57 # Dead umlaut +map +caps -altgr +shift 0x1A ~ 0xFE52 # Dead circumflex + From 568e0370122139c6f10791b8c4ff4d21cdc146b9 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 21:33:08 -0700 Subject: [PATCH 18/22] GUACAMOLE-518: Update Swiss French keymap to define behavior of Caps Lock. On Swiss French keyboards, Caps Lock affects only non-accented letters and is undone by Shift. --- src/protocols/rdp/keymaps/fr_ch_qwertz.keymap | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/protocols/rdp/keymaps/fr_ch_qwertz.keymap b/src/protocols/rdp/keymaps/fr_ch_qwertz.keymap index 73276614..8864d701 100644 --- a/src/protocols/rdp/keymaps/fr_ch_qwertz.keymap +++ b/src/protocols/rdp/keymaps/fr_ch_qwertz.keymap @@ -25,15 +25,25 @@ freerdp "KBD_SWISS_FRENCH" # Basic keys # -map -altgr -shift 0x29 0x02..0x0C ~ "§1234567890'" -map -altgr -shift 0x10..0x1A ~ "qwertzuiopè" -map -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjkléà$" -map -altgr -shift 0x56 0x2C..0x35 ~ "YXCVBNM;:_" +map -caps -altgr +shift 0x29 0x02..0x0C ~ "°+"*ç%&/()=?" +map -caps -altgr +shift 0x10..0x1B ~ "QWERTZUIOPü!" +map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLöä£" +map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">YXCVBNM;:_" + +map +caps -altgr -shift 0x29 0x02..0x0C ~ "§1234567890'" +map +caps -altgr -shift 0x10..0x1A ~ "QWERTZUIOPè" +map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLéà$" +map +caps -altgr -shift 0x56 0x2C..0x35 ~ "yxcvbnm;:_" # # Keys requiring AltGr From e8153f90020f27b6cb6d933c3fb6313679f985e4 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 21:46:57 -0700 Subject: [PATCH 19/22] GUACAMOLE-518: Update French keymap to define behavior of Caps Lock. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On French keyboards, Caps Lock behaves like Shift, affects all characters including dead keys but excluding "<", ">", "²", and any keys requiring AltGr, and is undone by Shift. --- src/protocols/rdp/keymaps/fr_fr_azerty.keymap | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/protocols/rdp/keymaps/fr_fr_azerty.keymap b/src/protocols/rdp/keymaps/fr_fr_azerty.keymap index ff5295ed..7f4f8327 100644 --- a/src/protocols/rdp/keymaps/fr_fr_azerty.keymap +++ b/src/protocols/rdp/keymaps/fr_fr_azerty.keymap @@ -25,18 +25,28 @@ freerdp "KBD_FRENCH" # Basic keys # -map -altgr -shift 0x29 0x02..0x0D ~ "²&é"'(-è_çà)=" -map -altgr -shift 0x10..0x19 0x1B ~ "azertyuiop$" -map -altgr -shift 0x1E..0x28 0x2B ~ "qsdfghjklmù*" -map -altgr -shift 0x56 0x2C..0x35 ~ "WXCVBN?./§" +map -caps -altgr +shift 0x02..0x0D ~ "1234567890°+" +map -caps -altgr +shift 0x10..0x19 0x1B ~ "AZERTYUIOP£" +map -caps -altgr +shift 0x1E..0x28 0x2B ~ "QSDFGHJKLM%µ" +map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">WXCVBN?./§" + +map +caps -altgr -shift 0x29 0x02..0x0D ~ "²1234567890°+" +map +caps -altgr -shift 0x10..0x19 0x1B ~ "AZERTYUIOP£" +map +caps -altgr -shift 0x1E..0x28 0x2B ~ "QSDFGHJKLM%µ" +map +caps -altgr -shift 0x56 0x2C..0x35 ~ "wxcvbn,;:!" # -# Keys requiring AltGr +# Keys requiring AltGr (unaffected by Caps Lock, but Shift must not be pressed) # map +altgr -shift 0x03..0x0D ~ "~#{[|`\^@]}" @@ -45,9 +55,12 @@ map +altgr -shift 0x12 ~ "€" map +altgr -shift 0x1B ~ "¤" # -# Dead keys +# Dead keys (affected by Caps Lock and Shift) # -map -altgr -shift 0x1A ~ 0xFE52 # Dead circumflex -map -altgr +shift 0x1A ~ 0xFE57 # Dead umlaut +map -caps -altgr -shift 0x1A ~ 0xFE52 # Dead circumflex +map -caps -altgr +shift 0x1A ~ 0xFE57 # Dead umlaut + +map +caps -altgr -shift 0x1A ~ 0xFE57 # Dead umlaut +map +caps -altgr +shift 0x1A ~ 0xFE52 # Dead circumflex From 3f375a45017e853553ce6d35cf2630be5db44600 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 21:57:00 -0700 Subject: [PATCH 20/22] GUACAMOLE-518: Update Swiss German keymap to define behavior of Caps Lock. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Swiss German keyboards, Caps Lock turns all letters uppercase and is undone by Shift, except for letters which instead rely on Shift to determine their identity (accented letters). For example, the key directly to the right of "P" produces "ü" if no modifiers or locks are active. With Shift pressed, the key changes identity to "è", with the state of Caps Lock determining the case: | Shift | Caps | Result | |-------|-------|--------| | | | "ü" | | | X | "Ü" | | X | | "è" | | X | X | "È" | This goes for all accented characters in the Swiss German layout. --- src/protocols/rdp/keymaps/de_ch_qwertz.keymap | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/protocols/rdp/keymaps/de_ch_qwertz.keymap b/src/protocols/rdp/keymaps/de_ch_qwertz.keymap index 59b9eba2..c4d80c7a 100644 --- a/src/protocols/rdp/keymaps/de_ch_qwertz.keymap +++ b/src/protocols/rdp/keymaps/de_ch_qwertz.keymap @@ -25,15 +25,25 @@ freerdp "KBD_SWISS_GERMAN" # Basic keys # -map -altgr -shift 0x29 0x02..0x0C ~ "§1234567890'" -map -altgr -shift 0x10..0x1A ~ "qwertzuiopü" -map -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklöä$" -map -altgr -shift 0x56 0x2C..0x35 ~ "YXCVBNM;:_" +map -caps -altgr +shift 0x29 0x02..0x0C ~ "°+"*ç%&/()=?" +map -caps -altgr +shift 0x10..0x1B ~ "QWERTZUIOPè!" +map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLéà£" +map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">YXCVBNM;:_" + +map +caps -altgr -shift 0x29 0x02..0x0C ~ "§1234567890'" +map +caps -altgr -shift 0x10..0x1A ~ "QWERTZUIOPÜ" +map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÖÄ$" +map +caps -altgr -shift 0x56 0x2C..0x35 ~ "yxcvbnm;:_" # # Keys requiring AltGr @@ -55,4 +65,4 @@ map +altgr -shift 0x0C ~ 0xFE51 # Dead acute map -altgr -shift 0x0D ~ 0xFE52 # Dead circumflex map -altgr +shift 0x0D ~ 0xFE50 # Dead grave map +altgr -shift 0x0D ~ 0xFE53 # Dead tilde -map -altgr -shift 0x1B ~ 0xFE57 # Dead umlaut \ No newline at end of file +map -altgr -shift 0x1B ~ 0xFE57 # Dead umlaut From a246403137709e26e1c11fd3ae4555d20c34021e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 22:16:59 -0700 Subject: [PATCH 21/22] GUACAMOLE-518: Update German keymap to define behavior of Caps Lock. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On German keyboards, Caps Lock behaves like Shift, affects all characters except dead keys, "°", "<", ">", "-", "_", and any keys requiring AltGr, and is undone by Shift. --- src/protocols/rdp/keymaps/de_de_qwertz.keymap | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/protocols/rdp/keymaps/de_de_qwertz.keymap b/src/protocols/rdp/keymaps/de_de_qwertz.keymap index 43b9722b..787c0559 100644 --- a/src/protocols/rdp/keymaps/de_de_qwertz.keymap +++ b/src/protocols/rdp/keymaps/de_de_qwertz.keymap @@ -25,15 +25,25 @@ freerdp "KBD_GERMAN" # Basic keys # -map -altgr -shift 0x02..0x0C ~ "1234567890ß" -map -altgr -shift 0x10..0x1B ~ "qwertzuiopü+" -map -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklöä#" -map -altgr -shift 0x56 0x2C..0x35 ~ "YXCVBNM;:_" +map -caps -altgr +shift 0x29 0x02..0x0C ~ "°!"§$%&/()=?" +map -caps -altgr +shift 0x10..0x1B ~ "QWERTZUIOPÜ*" +map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÖÄ'" +map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">YXCVBNM;:_" + +map +caps -altgr -shift 0x02..0x0C ~ "!"§$%&/()=?" +map +caps -altgr -shift 0x10..0x1B ~ "QWERTZUIOPÜ*" +map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKLÖÄ'" +map +caps -altgr -shift 0x56 0x2C..0x35 ~ "yxcvbnm,._" # # Keys requiring AltGr From 60944f109215c2f93db60b3a470b907c8f365bdb Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 22 Jun 2020 23:11:43 -0700 Subject: [PATCH 22/22] GUACAMOLE-518: Keep locally-tracked keyboard lock status in sync with remote changes to keyboard locks. --- src/protocols/rdp/keyboard.c | 21 +++++++++++++++++++++ src/protocols/rdp/keyboard.h | 18 ++++++++++++++++++ src/protocols/rdp/rdp.c | 3 +++ 3 files changed, 42 insertions(+) diff --git a/src/protocols/rdp/keyboard.c b/src/protocols/rdp/keyboard.c index 5bb925bf..a75b7340 100644 --- a/src/protocols/rdp/keyboard.c +++ b/src/protocols/rdp/keyboard.c @@ -709,3 +709,24 @@ void guac_rdp_keyboard_reset(guac_rdp_keyboard* keyboard) { } +BOOL guac_rdp_keyboard_set_indicators(rdpContext* context, UINT16 flags) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + + pthread_rwlock_rdlock(&(rdp_client->lock)); + + /* Skip if keyboard not yet ready */ + guac_rdp_keyboard* keyboard = rdp_client->keyboard; + if (keyboard == NULL) + goto complete; + + /* Update with received locks */ + guac_client_log(client, GUAC_LOG_DEBUG, "Received updated keyboard lock flags from RDP server: 0x%X", flags); + keyboard->lock_flags = flags; + +complete: + pthread_rwlock_unlock(&(rdp_client->lock)); + return TRUE; + +} diff --git a/src/protocols/rdp/keyboard.h b/src/protocols/rdp/keyboard.h index a07312bf..425f55c8 100644 --- a/src/protocols/rdp/keyboard.h +++ b/src/protocols/rdp/keyboard.h @@ -22,6 +22,7 @@ #include "keymap.h" +#include #include /** @@ -279,5 +280,22 @@ int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, */ void guac_rdp_keyboard_reset(guac_rdp_keyboard* keyboard); +/** + * Callback which is invoked by FreeRDP when the RDP server reports changes to + * keyboard lock status using a Server Set Keyboard Indicators PDU. + * + * @param context + * The rdpContext associated with the current RDP session. + * + * @param flags + * The remote state of all 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. + * + * @return + * TRUE if successful, FALSE otherwise. + */ +BOOL guac_rdp_keyboard_set_indicators(rdpContext* context, UINT16 flags); + #endif diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c index d7fd0611..3d7702c6 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -173,6 +173,9 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) { /* Beep on receipt of Play Sound PDU */ instance->update->PlaySound = guac_rdp_beep_play_sound; + /* Automatically synchronize keyboard locks when changed server-side */ + instance->update->SetKeyboardIndicators = guac_rdp_keyboard_set_indicators; + /* Set up GDI */ instance->update->DesktopResize = guac_rdp_gdi_desktop_resize; instance->update->EndPaint = guac_rdp_gdi_end_paint;