From 2207f11487cc819bdf7027cfeeac674036f8dec9 Mon Sep 17 00:00:00 2001 From: Dilbag Singh Date: Sat, 25 Oct 2025 07:53:45 -0700 Subject: [PATCH 1/2] GUACAMOLE-2161: Fix Ctrl/Shift scancode issues for Unicode (RU/EN) layouts in RDP. --- src/protocols/rdp/keyboard.c | 224 ++++++++++++++++++++++++++++++++++- 1 file changed, 223 insertions(+), 1 deletion(-) diff --git a/src/protocols/rdp/keyboard.c b/src/protocols/rdp/keyboard.c index 3e749bc78d..712694e8e0 100644 --- a/src/protocols/rdp/keyboard.c +++ b/src/protocols/rdp/keyboard.c @@ -27,7 +27,6 @@ #include #include #include - #include /** @@ -562,7 +561,229 @@ static void guac_rdp_keyboard_send_missing_key(guac_rdp_keyboard* keyboard, guac_client* client = keyboard->client; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + + /* If system modifiers (Ctrl/Alt) are held, prefer sending US scancode so + * that shortcuts like Ctrl+C/V/A work even in Unicode/failsafe layout. */ + int ctrl_or_alt_pressed = + guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LCTRL) + || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RCTRL) + || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LALT) + || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RALT); + + /* Normalize Cyrillic letters to Latin equivalents when Ctrl/Alt pressed + * so that shortcuts like Ctrl+C, Ctrl+V, etc. work even in Russian layout. + */ + if (ctrl_or_alt_pressed) { + + +/* Treat Ctrl or Alt (but NOT AltGr) as shortcut modifiers */ +int ctrl_pressed = + guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LCTRL) + || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RCTRL); + +int alt_pressed = + guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LALT) + || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RALT); + +/* AltGr appears as Ctrl+Alt together; don’t hijack that. */ +int altgr = ctrl_pressed && alt_pressed; + +/* We only force scancodes for real shortcuts: Ctrl-only OR Alt-only. */ +int shortcut_mod = !altgr && (ctrl_pressed || alt_pressed); + +if (shortcut_mod) { + /* Map both Latin and Russian keysyms for C/V to US scancodes. + * US Set 1 scancodes: C=0x2E, V=0x2F. + * This makes Ctrl+C / Ctrl+V work in ru-RU and en-US equally. + */ + int sc = 0; + + switch (keysym) { + /* --- Ctrl+C --- */ + case 'c': case 'C': + case 0x441: /* Cyrillic small 'с' */ + case 0x421: /* Cyrillic capital 'С' */ + sc = 0x2E; /* US 'C' key */ + break; + + /* --- Ctrl+V --- */ + case 'v': case 'V': + case 0x43C: /* Cyrillic small 'м' */ + case 0x41C: /* Cyrillic capital 'М' */ + sc = 0x2F; /* US 'V' key */ + break; + + default: + sc = 0; + } + if (sc) { + guac_client_log(client, GUAC_LOG_DEBUG, + "Shortcut: modifiers active, sending US scancode 0x%X for keysym 0x%X", + sc, keysym); + /* press + release with no extended flags */ + guac_rdp_send_key_event(rdp_client, sc, 0, 1); + guac_rdp_send_key_event(rdp_client, sc, 0, 0); + return; + } +} + switch (keysym) { + /* Lowercase Cyrillic */ + case 0x430: keysym = 'f'; break; // а + case 0x431: keysym = ','; break; // б + case 0x432: keysym = 'd'; break; // в + case 0x433: keysym = 'u'; break; // г + case 0x434: keysym = 'l'; break; // д + case 0x435: keysym = 't'; break; // е + case 0x436: keysym = ';'; break; // ж + case 0x437: keysym = 'p'; break; // з + case 0x438: keysym = 'b'; break; // и + case 0x439: keysym = 'q'; break; // й + case 0x43A: keysym = 'r'; break; // к + case 0x43B: keysym = 'k'; break; // л + case 0x43C: keysym = 'v'; break; // м + case 0x43D: keysym = 'y'; break; // н + case 0x43E: keysym = 'j'; break; // о + case 0x43F: keysym = 'g'; break; // п + case 0x440: keysym = 'h'; break; // р + case 0x441: keysym = 'c'; break; // с + case 0x442: keysym = 'n'; break; // т + case 0x443: keysym = 'e'; break; // у + case 0x444: keysym = 'a'; break; // ф + case 0x445: keysym = '['; break; // х + case 0x446: keysym = 'w'; break; // ц + case 0x447: keysym = 'x'; break; // ч + case 0x448: keysym = 'i'; break; // ш + case 0x449: keysym = 'o'; break; // щ + case 0x44A: keysym = ']'; break; // ъ + case 0x44B: keysym = 's'; break; // ы + case 0x44C: keysym = 'm'; break; // ь + case 0x44D: keysym = '\''; break; // э + case 0x44E: keysym = '.'; break; // ю + case 0x44F: keysym = 'z'; break; // я + + /* Uppercase Cyrillic */ + case 0x410: keysym = 'F'; break; // А + case 0x411: keysym = '<'; break; // Б + case 0x412: keysym = 'D'; break; // В + case 0x413: keysym = 'U'; break; // Г + case 0x414: keysym = 'L'; break; // Д + case 0x415: keysym = 'T'; break; // Е + case 0x416: keysym = ':'; break; // Ж + case 0x417: keysym = 'P'; break; // З + case 0x418: keysym = 'B'; break; // И + case 0x419: keysym = 'Q'; break; // Й + case 0x41A: keysym = 'R'; break; // К + case 0x41B: keysym = 'K'; break; // Л + case 0x41C: keysym = 'V'; break; // М + case 0x41D: keysym = 'Y'; break; // Н + case 0x41E: keysym = 'J'; break; // О + case 0x41F: keysym = 'G'; break; // П + case 0x420: keysym = 'H'; break; // Р + case 0x421: keysym = 'C'; break; // С + case 0x422: keysym = 'N'; break; // Т + case 0x423: keysym = 'E'; break; // У + case 0x424: keysym = 'A'; break; // Ф + case 0x425: keysym = '{'; break; // Х + case 0x426: keysym = 'W'; break; // Ц + case 0x427: keysym = 'X'; break; // Ч + case 0x428: keysym = 'I'; break; // Ш + case 0x429: keysym = 'O'; break; // Щ + case 0x42A: keysym = '}'; break; // Ъ + case 0x42B: keysym = 'S'; break; // Ы + case 0x42C: keysym = 'M'; break; // Ь + case 0x42D: keysym = '"'; break; // Э + case 0x42E: keysym = '>'; break; // Ю + case 0x42F: keysym = 'Z'; break; // Я + } + } + + if (ctrl_or_alt_pressed) { + /* Map ASCII letters/digits to PC/AT set 1 scancodes (US layout). + * This is enough for common shortcuts (A, C, V, X, Z, Y, ...). */ + int sc = 0; + /* Normalize X11-style keysyms (0x1000000 + Unicode) to plain Unicode */ + if ((keysym & 0xFF000000) == 0x01000000) + keysym &= 0x00FFFFFF; + + switch (keysym) { + /* digits 0-9 */ + case '1': sc = 0x02; break; case '2': sc = 0x03; break; + case '3': sc = 0x04; break; case '4': sc = 0x05; break; + case '5': sc = 0x06; break; case '6': sc = 0x07; break; + case '7': sc = 0x08; break; case '8': sc = 0x09; break; + case '9': sc = 0x0A; break; case '0': sc = 0x0B; break; + + /* Latin letters A-Z (both cases) */ + case 'a': case 'A': sc = 0x1E; break; + case 'b': case 'B': sc = 0x30; break; + case 'c': case 'C': sc = 0x2E; break; + case 'd': case 'D': sc = 0x20; break; + case 'e': case 'E': sc = 0x12; break; + case 'f': case 'F': sc = 0x21; break; + case 'g': case 'G': sc = 0x22; break; + case 'h': case 'H': sc = 0x23; break; + case 'i': case 'I': sc = 0x17; break; + case 'j': case 'J': sc = 0x24; break; + case 'k': case 'K': sc = 0x25; break; + case 'l': case 'L': sc = 0x26; break; + case 'm': case 'M': sc = 0x32; break; + case 'n': case 'N': sc = 0x31; break; + case 'o': case 'O': sc = 0x18; break; + case 'p': case 'P': sc = 0x19; break; + case 'q': case 'Q': sc = 0x10; break; + case 'r': case 'R': sc = 0x13; break; + case 's': case 'S': sc = 0x1F; break; + case 't': case 'T': sc = 0x14; break; + case 'u': case 'U': sc = 0x16; break; + case 'v': case 'V': sc = 0x2F; break; + case 'w': case 'W': sc = 0x11; break; + case 'x': case 'X': sc = 0x2D; break; + case 'y': case 'Y': sc = 0x15; break; + case 'z': case 'Z': sc = 0x2C; break; + + /* Russian Cyrillic letters - most common shortcuts */ + /* Lowercase Cyrillic */ + case 0x0444: case 0x0424: sc = 0x1E; break; /* ф/Ф -> A */ + case 0x0438: case 0x0418: sc = 0x30; break; /* и/И -> B */ + case 0x0441: case 0x0421: sc = 0x2E; break; /* с/С -> C */ + case 0x0432: case 0x0412: sc = 0x20; break; /* в/В -> D */ + case 0x0443: case 0x0423: sc = 0x12; break; /* у/У -> E */ + case 0x0430: case 0x0410: sc = 0x21; break; /* а/А -> F */ + case 0x043f: case 0x041f: sc = 0x22; break; /* п/П -> G */ + case 0x0440: case 0x0420: sc = 0x23; break; /* р/Р -> H */ + case 0x0448: case 0x0428: sc = 0x17; break; /* ш/Ш -> I */ + case 0x043e: case 0x041e: sc = 0x24; break; /* о/О -> J */ + case 0x043b: case 0x041b: sc = 0x25; break; /* л/Л -> K */ + case 0x0434: case 0x0414: sc = 0x26; break; /* д/Д -> L */ + case 0x044c: case 0x042c: sc = 0x32; break; /* ь/Ь -> M */ + case 0x0442: case 0x0422: sc = 0x31; break; /* т/Т -> N */ + case 0x0449: case 0x0429: sc = 0x18; break; /* щ/Щ -> O */ + case 0x0437: case 0x0417: sc = 0x19; break; /* з/З -> P */ + case 0x0439: case 0x0419: sc = 0x10; break; /* й/Й -> Q */ + case 0x043a: case 0x041a: sc = 0x13; break; /* к/К -> R */ + case 0x044b: case 0x042b: sc = 0x1F; break; /* ы/Ы -> S */ + case 0x0435: case 0x0415: sc = 0x14; break; /* е/Е -> T */ + case 0x0433: case 0x0413: sc = 0x16; break; /* г/Г -> U */ + case 0x043c: case 0x041c: sc = 0x2F; break; /* м/М -> V */ + case 0x0446: case 0x0426: sc = 0x11; break; /* ц/Ц -> W */ + case 0x0447: case 0x0427: sc = 0x2D; break; /* ч/Ч -> X */ + case 0x043d: case 0x041d: sc = 0x15; break; /* н/Н -> Y */ + case 0x044f: case 0x042f: sc = 0x2C; break; /* я/Я -> Z */ + + + default: sc = 0; break; + } + if (sc) { + guac_client_log(client, GUAC_LOG_DEBUG, + "Sending keysym 0x%x as US scancode 0x%X due to modifiers", + keysym, sc); + /* send press+release; 'flags' = 0 for regular keys */ + guac_rdp_send_key_event(rdp_client, sc, 0, 1); + guac_rdp_send_key_event(rdp_client, sc, 0, 0); + return; + } + } /* Attempt to type using dead keys */ if (!guac_rdp_decompose_keysym(keyboard, keysym)) return; @@ -738,3 +959,4 @@ BOOL guac_rdp_keyboard_set_indicators(rdpContext* context, UINT16 flags) { return TRUE; } + From 31ba6ddcacb3869dc402e6cbde2c6c073532c9a2 Mon Sep 17 00:00:00 2001 From: Dilbag Singh Date: Sun, 26 Oct 2025 00:52:21 -0700 Subject: [PATCH 2/2] GUACAMOLE-2161: Fix Unicode (RU/EN) Ctrl/Shift handling and clean up formatting --- src/protocols/rdp/keyboard.c | 1000 ++++++++++++++++++++++------------ 1 file changed, 666 insertions(+), 334 deletions(-) diff --git a/src/protocols/rdp/keyboard.c b/src/protocols/rdp/keyboard.c index 712694e8e0..ba65075cbc 100644 --- a/src/protocols/rdp/keyboard.c +++ b/src/protocols/rdp/keyboard.c @@ -41,32 +41,32 @@ * The RDP lock flag which corresponds to the given keysym, or zero if the * given keysym does not represent a lock key. */ -static int guac_rdp_keyboard_lock_flag(int keysym) { +static int guac_rdp_keyboard_lock_flag(int keysym) +{ /* Translate keysym into corresponding lock flag */ - switch (keysym) { + switch (keysym) + { - /* Scroll lock */ - case GUAC_RDP_KEYSYM_SCROLL_LOCK: - return KBD_SYNC_SCROLL_LOCK; + /* Scroll lock */ + case GUAC_RDP_KEYSYM_SCROLL_LOCK: + return KBD_SYNC_SCROLL_LOCK; - /* Kana lock */ - case GUAC_RDP_KEYSYM_KANA_LOCK: - return KBD_SYNC_KANA_LOCK; + /* Kana lock */ + case GUAC_RDP_KEYSYM_KANA_LOCK: + return KBD_SYNC_KANA_LOCK; - /* Num lock */ - case GUAC_RDP_KEYSYM_NUM_LOCK: - return KBD_SYNC_NUM_LOCK; - - /* Caps lock */ - case GUAC_RDP_KEYSYM_CAPS_LOCK: - return KBD_SYNC_CAPS_LOCK; + /* Num lock */ + case GUAC_RDP_KEYSYM_NUM_LOCK: + return KBD_SYNC_NUM_LOCK; + /* Caps lock */ + case GUAC_RDP_KEYSYM_CAPS_LOCK: + return KBD_SYNC_CAPS_LOCK; } /* Not a lock key */ return 0; - } /** @@ -89,8 +89,9 @@ static int guac_rdp_keyboard_lock_flag(int keysym) { * @param pressed * Non-zero if the key is being pressed, zero if the key is being released. */ -static void guac_rdp_send_key_event(guac_rdp_client* rdp_client, - int scancode, int flags, int pressed) { +static void guac_rdp_send_key_event(guac_rdp_client *rdp_client, + int scancode, int flags, int pressed) +{ /* Determine proper event flag for pressed state */ int pressed_flags; @@ -100,16 +101,15 @@ static void guac_rdp_send_key_event(guac_rdp_client* rdp_client, pressed_flags = KBD_FLAGS_RELEASE; /* Skip if not yet connected */ - freerdp* rdp_inst = rdp_client->rdp_inst; + freerdp *rdp_inst = rdp_client->rdp_inst; if (rdp_inst == NULL) return; /* Send actual key */ pthread_mutex_lock(&(rdp_client->message_lock)); GUAC_RDP_CONTEXT(rdp_inst)->input->KeyboardEvent( - GUAC_RDP_CONTEXT(rdp_inst)->input, flags | pressed_flags, scancode); + GUAC_RDP_CONTEXT(rdp_inst)->input, flags | pressed_flags, scancode); pthread_mutex_unlock(&(rdp_client->message_lock)); - } /** @@ -126,20 +126,20 @@ static void guac_rdp_send_key_event(guac_rdp_client* rdp_client, * The Unicode codepoint of the character being input via the Unicode * event. */ -static void guac_rdp_send_unicode_event(guac_rdp_client* rdp_client, - int codepoint) { +static void guac_rdp_send_unicode_event(guac_rdp_client *rdp_client, + int codepoint) +{ /* Skip if not yet connected */ - freerdp* rdp_inst = rdp_client->rdp_inst; + freerdp *rdp_inst = rdp_client->rdp_inst; if (rdp_inst == NULL) return; /* Send Unicode event */ pthread_mutex_lock(&(rdp_client->message_lock)); GUAC_RDP_CONTEXT(rdp_inst)->input->UnicodeKeyboardEvent( - GUAC_RDP_CONTEXT(rdp_inst)->input, 0, codepoint); + GUAC_RDP_CONTEXT(rdp_inst)->input, 0, codepoint); pthread_mutex_unlock(&(rdp_client->message_lock)); - } /** @@ -156,20 +156,20 @@ static void guac_rdp_send_unicode_event(guac_rdp_client* rdp_client, * if any, as dictated by the RDP protocol. If no flags are set, then no * lock keys will be active. */ -static void guac_rdp_send_synchronize_event(guac_rdp_client* rdp_client, - UINT32 flags) { +static void guac_rdp_send_synchronize_event(guac_rdp_client *rdp_client, + UINT32 flags) +{ /* Skip if not yet connected */ - freerdp* rdp_inst = rdp_client->rdp_inst; + freerdp *rdp_inst = rdp_client->rdp_inst; if (rdp_inst == NULL) return; /* Synchronize lock key states */ pthread_mutex_lock(&(rdp_client->message_lock)); GUAC_RDP_CONTEXT(rdp_inst)->input->SynchronizeEvent( - GUAC_RDP_CONTEXT(rdp_inst)->input, flags); + GUAC_RDP_CONTEXT(rdp_inst)->input, flags); pthread_mutex_unlock(&(rdp_client->message_lock)); - } /** @@ -190,8 +190,9 @@ static void guac_rdp_send_synchronize_event(guac_rdp_client* rdp_client, * 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, - int keysym) { +static guac_rdp_key **guac_rdp_keyboard_map_key(guac_rdp_keyboard *keyboard, + int keysym) +{ int index; @@ -209,7 +210,6 @@ static guac_rdp_key** guac_rdp_keyboard_map_key(guac_rdp_keyboard* keyboard, /* Corresponding key mapping (defined or not) has been located */ return &(keyboard->keys_by_keysym[index]); - } /** @@ -222,17 +222,18 @@ static guac_rdp_key** guac_rdp_keyboard_map_key(guac_rdp_keyboard* keyboard, * @return * The number of bits that are set within the given integer. */ -static int guac_rdp_count_bits(unsigned int value) { +static int guac_rdp_count_bits(unsigned int value) +{ int bits = 0; - while (value) { + while (value) + { bits += value & 1; value >>= 1; } return bits; - } /** @@ -259,8 +260,9 @@ static int guac_rdp_count_bits(unsigned int value) { * 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) { +static int guac_rdp_keyboard_get_cost(guac_rdp_keyboard *keyboard, + const guac_rdp_keysym_desc *def) +{ unsigned int modifier_flags = guac_rdp_keyboard_get_modifier_flags(keyboard); @@ -276,7 +278,6 @@ static int guac_rdp_keyboard_get_cost(guac_rdp_keyboard* keyboard, cost += guac_rdp_count_bits(update_modifiers); return cost; - } /** @@ -295,16 +296,16 @@ static int guac_rdp_keyboard_get_cost(guac_rdp_keyboard* keyboard, * 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) { +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_by_keysym = guac_rdp_keyboard_map_key(keyboard, keysym); + guac_rdp_key **key_by_keysym = guac_rdp_keyboard_map_key(keyboard, keysym); if (key_by_keysym == NULL) return NULL; return *key_by_keysym; - } /** @@ -322,8 +323,9 @@ static guac_rdp_key* guac_rdp_keyboard_get_key(guac_rdp_keyboard* keyboard, * 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) { +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) @@ -331,25 +333,25 @@ static const guac_rdp_keysym_desc* guac_rdp_keyboard_get_definition(guac_rdp_key /* 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]; + 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++) { + for (int i = 1; i < key->num_definitions; i++) + { - const guac_rdp_keysym_desc* def = key->definitions[i]; + const guac_rdp_keysym_desc *def = key->definitions[i]; int cost = guac_rdp_keyboard_get_cost(keyboard, def); - if (cost < best_cost) { + if (cost < best_cost) + { best_def = def; best_cost = cost; } - } return best_def; - } /** @@ -365,47 +367,50 @@ static const guac_rdp_keysym_desc* guac_rdp_keyboard_get_definition(guac_rdp_key * @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) { +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_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 (*key_by_keysym == NULL) + { - if (keyboard->num_keys == GUAC_RDP_KEYBOARD_MAX_KEYSYMS) { + 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); + "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; + guac_rdp_key *key = *key_by_keysym; /* Add new definition only if sufficient space remains */ - if (key->num_definitions == GUAC_RDP_KEY_MAX_DEFINITIONS) { + 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); + "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; - } /** @@ -422,8 +427,9 @@ static void guac_rdp_keyboard_add_mapping(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, - const guac_rdp_keymap* keymap) { +static void guac_rdp_keyboard_load_keymap(guac_rdp_keyboard *keyboard, + const guac_rdp_keymap *keymap) +{ /* If parent exists, load parent first */ if (keymap->parent != NULL) @@ -431,69 +437,66 @@ static void guac_rdp_keyboard_load_keymap(guac_rdp_keyboard* keyboard, /* Log load */ guac_client_log(keyboard->client, GUAC_LOG_INFO, - "Loading keymap \"%s\"", keymap->name); + "Loading keymap \"%s\"", keymap->name); /* Copy mapping into keymap */ - const guac_rdp_keysym_desc* mapping = keymap->mapping; - while (mapping->keysym != 0) { + const guac_rdp_keysym_desc *mapping = keymap->mapping; + while (mapping->keysym != 0) + { guac_rdp_keyboard_add_mapping(keyboard, mapping++); } - } -guac_rdp_keyboard* guac_rdp_keyboard_alloc(guac_client* client, - const guac_rdp_keymap* keymap) { +guac_rdp_keyboard *guac_rdp_keyboard_alloc(guac_client *client, + const guac_rdp_keymap *keymap) +{ - guac_rdp_keyboard* keyboard = guac_mem_zalloc(sizeof(guac_rdp_keyboard)); + guac_rdp_keyboard *keyboard = guac_mem_zalloc(sizeof(guac_rdp_keyboard)); keyboard->client = client; /* Load keymap into keyboard */ guac_rdp_keyboard_load_keymap(keyboard, keymap); return keyboard; - } -void guac_rdp_keyboard_free(guac_rdp_keyboard* keyboard) { +void guac_rdp_keyboard_free(guac_rdp_keyboard *keyboard) +{ guac_mem_free(keyboard); } -int guac_rdp_keyboard_is_defined(guac_rdp_keyboard* keyboard, int keysym) { +int guac_rdp_keyboard_is_defined(guac_rdp_keyboard *keyboard, int keysym) +{ /* Return whether the mapping actually exists */ return guac_rdp_keyboard_get_key(keyboard, keysym) != NULL; - } -int guac_rdp_keyboard_is_pressed(guac_rdp_keyboard* keyboard, int keysym) { +int guac_rdp_keyboard_is_pressed(guac_rdp_keyboard *keyboard, int keysym) +{ - guac_rdp_key* key = guac_rdp_keyboard_get_key(keyboard, keysym); + guac_rdp_key *key = guac_rdp_keyboard_get_key(keyboard, keysym); return key != NULL && key->pressed != NULL; - } -unsigned int guac_rdp_keyboard_get_modifier_flags(guac_rdp_keyboard* keyboard) { +unsigned int guac_rdp_keyboard_get_modifier_flags(guac_rdp_keyboard *keyboard) +{ unsigned int modifier_flags = 0; /* Shift */ - if (guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LSHIFT) - || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RSHIFT)) + if (guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LSHIFT) || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RSHIFT)) modifier_flags |= GUAC_RDP_KEYMAP_MODIFIER_SHIFT; /* Dedicated AltGr key */ - if (guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RALT) - || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_ALTGR)) + if (guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RALT) || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_ALTGR)) modifier_flags |= GUAC_RDP_KEYMAP_MODIFIER_ALTGR; /* AltGr via Ctrl+Alt */ - if (guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LALT) - && (guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RCTRL) - || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LCTRL))) + if (guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LALT) && (guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RCTRL) || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LCTRL))) modifier_flags |= GUAC_RDP_KEYMAP_MODIFIER_ALTGR; return modifier_flags; - } /** @@ -514,34 +517,35 @@ unsigned int guac_rdp_keyboard_get_modifier_flags(guac_rdp_keyboard* keyboard) { * Zero if the key was successfully pressed/released, non-zero if the key * cannot be sent using RDP key events. */ -static const guac_rdp_keysym_desc* guac_rdp_keyboard_send_defined_key(guac_rdp_keyboard* keyboard, - guac_rdp_key* key, int pressed) { +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; + guac_client *client = keyboard->client; + guac_rdp_client *rdp_client = (guac_rdp_client *)client->data; - const guac_rdp_keysym_desc* keysym_desc = guac_rdp_keyboard_get_definition(keyboard, key); + 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 */ - if (pressed) { + if (pressed) + { guac_rdp_keyboard_update_locks(keyboard, - keysym_desc->set_locks, - keysym_desc->clear_locks); + keysym_desc->set_locks, + keysym_desc->clear_locks); guac_rdp_keyboard_update_modifiers(keyboard, - keysym_desc->set_modifiers, - keysym_desc->clear_modifiers); + 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); + keysym_desc->flags, pressed); return keysym_desc; - } /** @@ -556,228 +560,543 @@ static const guac_rdp_keysym_desc* guac_rdp_keyboard_send_defined_key(guac_rdp_k * @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) { +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; - guac_client* client = keyboard->client; - guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; - /* If system modifiers (Ctrl/Alt) are held, prefer sending US scancode so * that shortcuts like Ctrl+C/V/A work even in Unicode/failsafe layout. */ int ctrl_or_alt_pressed = - guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LCTRL) - || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RCTRL) - || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LALT) - || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RALT); + guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LCTRL) || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RCTRL) || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LALT) || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RALT); /* Normalize Cyrillic letters to Latin equivalents when Ctrl/Alt pressed * so that shortcuts like Ctrl+C, Ctrl+V, etc. work even in Russian layout. */ - if (ctrl_or_alt_pressed) { + if (ctrl_or_alt_pressed) + { - -/* Treat Ctrl or Alt (but NOT AltGr) as shortcut modifiers */ -int ctrl_pressed = - guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LCTRL) - || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RCTRL); + /* Treat Ctrl or Alt (but NOT AltGr) as shortcut modifiers */ + int ctrl_pressed = + guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LCTRL) || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RCTRL); -int alt_pressed = - guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LALT) - || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RALT); + int alt_pressed = + guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_LALT) || guac_rdp_keyboard_is_pressed(keyboard, GUAC_RDP_KEYSYM_RALT); -/* AltGr appears as Ctrl+Alt together; don’t hijack that. */ -int altgr = ctrl_pressed && alt_pressed; + /* AltGr appears as Ctrl+Alt together; don’t hijack that. */ + int altgr = ctrl_pressed && alt_pressed; -/* We only force scancodes for real shortcuts: Ctrl-only OR Alt-only. */ -int shortcut_mod = !altgr && (ctrl_pressed || alt_pressed); + /* We only force scancodes for real shortcuts: Ctrl-only OR Alt-only. */ + int shortcut_mod = !altgr && (ctrl_pressed || alt_pressed); -if (shortcut_mod) { - /* Map both Latin and Russian keysyms for C/V to US scancodes. - * US Set 1 scancodes: C=0x2E, V=0x2F. - * This makes Ctrl+C / Ctrl+V work in ru-RU and en-US equally. - */ - int sc = 0; + if (shortcut_mod) + { + /* Map both Latin and Russian keysyms for C/V to US scancodes. + * US Set 1 scancodes: C=0x2E, V=0x2F. + * This makes Ctrl+C / Ctrl+V work in ru-RU and en-US equally. + */ + int sc = 0; - switch (keysym) { + switch (keysym) + { /* --- Ctrl+C --- */ - case 'c': case 'C': - case 0x441: /* Cyrillic small 'с' */ - case 0x421: /* Cyrillic capital 'С' */ + case 'c': + case 'C': + case 0x441: /* Cyrillic small 'с' */ + case 0x421: /* Cyrillic capital 'С' */ sc = 0x2E; /* US 'C' key */ break; /* --- Ctrl+V --- */ - case 'v': case 'V': - case 0x43C: /* Cyrillic small 'м' */ - case 0x41C: /* Cyrillic capital 'М' */ + case 'v': + case 'V': + case 0x43C: /* Cyrillic small 'м' */ + case 0x41C: /* Cyrillic capital 'М' */ sc = 0x2F; /* US 'V' key */ break; default: sc = 0; + } + + if (sc) + { + guac_client_log(client, GUAC_LOG_DEBUG, + "Shortcut: modifiers active, sending US scancode 0x%X for keysym 0x%X", + sc, keysym); + /* press + release with no extended flags */ + guac_rdp_send_key_event(rdp_client, sc, 0, 1); + guac_rdp_send_key_event(rdp_client, sc, 0, 0); + return; + } } - - if (sc) { - guac_client_log(client, GUAC_LOG_DEBUG, - "Shortcut: modifiers active, sending US scancode 0x%X for keysym 0x%X", - sc, keysym); - /* press + release with no extended flags */ - guac_rdp_send_key_event(rdp_client, sc, 0, 1); - guac_rdp_send_key_event(rdp_client, sc, 0, 0); - return; - } -} - switch (keysym) { - /* Lowercase Cyrillic */ - case 0x430: keysym = 'f'; break; // а - case 0x431: keysym = ','; break; // б - case 0x432: keysym = 'd'; break; // в - case 0x433: keysym = 'u'; break; // г - case 0x434: keysym = 'l'; break; // д - case 0x435: keysym = 't'; break; // е - case 0x436: keysym = ';'; break; // ж - case 0x437: keysym = 'p'; break; // з - case 0x438: keysym = 'b'; break; // и - case 0x439: keysym = 'q'; break; // й - case 0x43A: keysym = 'r'; break; // к - case 0x43B: keysym = 'k'; break; // л - case 0x43C: keysym = 'v'; break; // м - case 0x43D: keysym = 'y'; break; // н - case 0x43E: keysym = 'j'; break; // о - case 0x43F: keysym = 'g'; break; // п - case 0x440: keysym = 'h'; break; // р - case 0x441: keysym = 'c'; break; // с - case 0x442: keysym = 'n'; break; // т - case 0x443: keysym = 'e'; break; // у - case 0x444: keysym = 'a'; break; // ф - case 0x445: keysym = '['; break; // х - case 0x446: keysym = 'w'; break; // ц - case 0x447: keysym = 'x'; break; // ч - case 0x448: keysym = 'i'; break; // ш - case 0x449: keysym = 'o'; break; // щ - case 0x44A: keysym = ']'; break; // ъ - case 0x44B: keysym = 's'; break; // ы - case 0x44C: keysym = 'm'; break; // ь - case 0x44D: keysym = '\''; break; // э - case 0x44E: keysym = '.'; break; // ю - case 0x44F: keysym = 'z'; break; // я - - /* Uppercase Cyrillic */ - case 0x410: keysym = 'F'; break; // А - case 0x411: keysym = '<'; break; // Б - case 0x412: keysym = 'D'; break; // В - case 0x413: keysym = 'U'; break; // Г - case 0x414: keysym = 'L'; break; // Д - case 0x415: keysym = 'T'; break; // Е - case 0x416: keysym = ':'; break; // Ж - case 0x417: keysym = 'P'; break; // З - case 0x418: keysym = 'B'; break; // И - case 0x419: keysym = 'Q'; break; // Й - case 0x41A: keysym = 'R'; break; // К - case 0x41B: keysym = 'K'; break; // Л - case 0x41C: keysym = 'V'; break; // М - case 0x41D: keysym = 'Y'; break; // Н - case 0x41E: keysym = 'J'; break; // О - case 0x41F: keysym = 'G'; break; // П - case 0x420: keysym = 'H'; break; // Р - case 0x421: keysym = 'C'; break; // С - case 0x422: keysym = 'N'; break; // Т - case 0x423: keysym = 'E'; break; // У - case 0x424: keysym = 'A'; break; // Ф - case 0x425: keysym = '{'; break; // Х - case 0x426: keysym = 'W'; break; // Ц - case 0x427: keysym = 'X'; break; // Ч - case 0x428: keysym = 'I'; break; // Ш - case 0x429: keysym = 'O'; break; // Щ - case 0x42A: keysym = '}'; break; // Ъ - case 0x42B: keysym = 'S'; break; // Ы - case 0x42C: keysym = 'M'; break; // Ь - case 0x42D: keysym = '"'; break; // Э - case 0x42E: keysym = '>'; break; // Ю - case 0x42F: keysym = 'Z'; break; // Я + switch (keysym) + { + /* Lowercase Cyrillic */ + case 0x430: + keysym = 'f'; + break; // а + case 0x431: + keysym = ','; + break; // б + case 0x432: + keysym = 'd'; + break; // в + case 0x433: + keysym = 'u'; + break; // г + case 0x434: + keysym = 'l'; + break; // д + case 0x435: + keysym = 't'; + break; // е + case 0x436: + keysym = ';'; + break; // ж + case 0x437: + keysym = 'p'; + break; // з + case 0x438: + keysym = 'b'; + break; // и + case 0x439: + keysym = 'q'; + break; // й + case 0x43A: + keysym = 'r'; + break; // к + case 0x43B: + keysym = 'k'; + break; // л + case 0x43C: + keysym = 'v'; + break; // м + case 0x43D: + keysym = 'y'; + break; // н + case 0x43E: + keysym = 'j'; + break; // о + case 0x43F: + keysym = 'g'; + break; // п + case 0x440: + keysym = 'h'; + break; // р + case 0x441: + keysym = 'c'; + break; // с + case 0x442: + keysym = 'n'; + break; // т + case 0x443: + keysym = 'e'; + break; // у + case 0x444: + keysym = 'a'; + break; // ф + case 0x445: + keysym = '['; + break; // х + case 0x446: + keysym = 'w'; + break; // ц + case 0x447: + keysym = 'x'; + break; // ч + case 0x448: + keysym = 'i'; + break; // ш + case 0x449: + keysym = 'o'; + break; // щ + case 0x44A: + keysym = ']'; + break; // ъ + case 0x44B: + keysym = 's'; + break; // ы + case 0x44C: + keysym = 'm'; + break; // ь + case 0x44D: + keysym = '\''; + break; // э + case 0x44E: + keysym = '.'; + break; // ю + case 0x44F: + keysym = 'z'; + break; // я + + /* Uppercase Cyrillic */ + case 0x410: + keysym = 'F'; + break; // А + case 0x411: + keysym = '<'; + break; // Б + case 0x412: + keysym = 'D'; + break; // В + case 0x413: + keysym = 'U'; + break; // Г + case 0x414: + keysym = 'L'; + break; // Д + case 0x415: + keysym = 'T'; + break; // Е + case 0x416: + keysym = ':'; + break; // Ж + case 0x417: + keysym = 'P'; + break; // З + case 0x418: + keysym = 'B'; + break; // И + case 0x419: + keysym = 'Q'; + break; // Й + case 0x41A: + keysym = 'R'; + break; // К + case 0x41B: + keysym = 'K'; + break; // Л + case 0x41C: + keysym = 'V'; + break; // М + case 0x41D: + keysym = 'Y'; + break; // Н + case 0x41E: + keysym = 'J'; + break; // О + case 0x41F: + keysym = 'G'; + break; // П + case 0x420: + keysym = 'H'; + break; // Р + case 0x421: + keysym = 'C'; + break; // С + case 0x422: + keysym = 'N'; + break; // Т + case 0x423: + keysym = 'E'; + break; // У + case 0x424: + keysym = 'A'; + break; // Ф + case 0x425: + keysym = '{'; + break; // Х + case 0x426: + keysym = 'W'; + break; // Ц + case 0x427: + keysym = 'X'; + break; // Ч + case 0x428: + keysym = 'I'; + break; // Ш + case 0x429: + keysym = 'O'; + break; // Щ + case 0x42A: + keysym = '}'; + break; // Ъ + case 0x42B: + keysym = 'S'; + break; // Ы + case 0x42C: + keysym = 'M'; + break; // Ь + case 0x42D: + keysym = '"'; + break; // Э + case 0x42E: + keysym = '>'; + break; // Ю + case 0x42F: + keysym = 'Z'; + break; // Я } } - if (ctrl_or_alt_pressed) { + if (ctrl_or_alt_pressed) + { /* Map ASCII letters/digits to PC/AT set 1 scancodes (US layout). * This is enough for common shortcuts (A, C, V, X, Z, Y, ...). */ int sc = 0; - /* Normalize X11-style keysyms (0x1000000 + Unicode) to plain Unicode */ + /* Normalize X11-style keysyms (0x1000000 + Unicode) to plain Unicode */ if ((keysym & 0xFF000000) == 0x01000000) keysym &= 0x00FFFFFF; - switch (keysym) { - /* digits 0-9 */ - case '1': sc = 0x02; break; case '2': sc = 0x03; break; - case '3': sc = 0x04; break; case '4': sc = 0x05; break; - case '5': sc = 0x06; break; case '6': sc = 0x07; break; - case '7': sc = 0x08; break; case '8': sc = 0x09; break; - case '9': sc = 0x0A; break; case '0': sc = 0x0B; break; - - /* Latin letters A-Z (both cases) */ - case 'a': case 'A': sc = 0x1E; break; - case 'b': case 'B': sc = 0x30; break; - case 'c': case 'C': sc = 0x2E; break; - case 'd': case 'D': sc = 0x20; break; - case 'e': case 'E': sc = 0x12; break; - case 'f': case 'F': sc = 0x21; break; - case 'g': case 'G': sc = 0x22; break; - case 'h': case 'H': sc = 0x23; break; - case 'i': case 'I': sc = 0x17; break; - case 'j': case 'J': sc = 0x24; break; - case 'k': case 'K': sc = 0x25; break; - case 'l': case 'L': sc = 0x26; break; - case 'm': case 'M': sc = 0x32; break; - case 'n': case 'N': sc = 0x31; break; - case 'o': case 'O': sc = 0x18; break; - case 'p': case 'P': sc = 0x19; break; - case 'q': case 'Q': sc = 0x10; break; - case 'r': case 'R': sc = 0x13; break; - case 's': case 'S': sc = 0x1F; break; - case 't': case 'T': sc = 0x14; break; - case 'u': case 'U': sc = 0x16; break; - case 'v': case 'V': sc = 0x2F; break; - case 'w': case 'W': sc = 0x11; break; - case 'x': case 'X': sc = 0x2D; break; - case 'y': case 'Y': sc = 0x15; break; - case 'z': case 'Z': sc = 0x2C; break; - - /* Russian Cyrillic letters - most common shortcuts */ - /* Lowercase Cyrillic */ - case 0x0444: case 0x0424: sc = 0x1E; break; /* ф/Ф -> A */ - case 0x0438: case 0x0418: sc = 0x30; break; /* и/И -> B */ - case 0x0441: case 0x0421: sc = 0x2E; break; /* с/С -> C */ - case 0x0432: case 0x0412: sc = 0x20; break; /* в/В -> D */ - case 0x0443: case 0x0423: sc = 0x12; break; /* у/У -> E */ - case 0x0430: case 0x0410: sc = 0x21; break; /* а/А -> F */ - case 0x043f: case 0x041f: sc = 0x22; break; /* п/П -> G */ - case 0x0440: case 0x0420: sc = 0x23; break; /* р/Р -> H */ - case 0x0448: case 0x0428: sc = 0x17; break; /* ш/Ш -> I */ - case 0x043e: case 0x041e: sc = 0x24; break; /* о/О -> J */ - case 0x043b: case 0x041b: sc = 0x25; break; /* л/Л -> K */ - case 0x0434: case 0x0414: sc = 0x26; break; /* д/Д -> L */ - case 0x044c: case 0x042c: sc = 0x32; break; /* ь/Ь -> M */ - case 0x0442: case 0x0422: sc = 0x31; break; /* т/Т -> N */ - case 0x0449: case 0x0429: sc = 0x18; break; /* щ/Щ -> O */ - case 0x0437: case 0x0417: sc = 0x19; break; /* з/З -> P */ - case 0x0439: case 0x0419: sc = 0x10; break; /* й/Й -> Q */ - case 0x043a: case 0x041a: sc = 0x13; break; /* к/К -> R */ - case 0x044b: case 0x042b: sc = 0x1F; break; /* ы/Ы -> S */ - case 0x0435: case 0x0415: sc = 0x14; break; /* е/Е -> T */ - case 0x0433: case 0x0413: sc = 0x16; break; /* г/Г -> U */ - case 0x043c: case 0x041c: sc = 0x2F; break; /* м/М -> V */ - case 0x0446: case 0x0426: sc = 0x11; break; /* ц/Ц -> W */ - case 0x0447: case 0x0427: sc = 0x2D; break; /* ч/Ч -> X */ - case 0x043d: case 0x041d: sc = 0x15; break; /* н/Н -> Y */ - case 0x044f: case 0x042f: sc = 0x2C; break; /* я/Я -> Z */ - - - default: sc = 0; break; + switch (keysym) + { + /* digits 0-9 */ + case '1': + sc = 0x02; + break; + case '2': + sc = 0x03; + break; + case '3': + sc = 0x04; + break; + case '4': + sc = 0x05; + break; + case '5': + sc = 0x06; + break; + case '6': + sc = 0x07; + break; + case '7': + sc = 0x08; + break; + case '8': + sc = 0x09; + break; + case '9': + sc = 0x0A; + break; + case '0': + sc = 0x0B; + break; + + /* Latin letters A-Z (both cases) */ + case 'a': + case 'A': + sc = 0x1E; + break; + case 'b': + case 'B': + sc = 0x30; + break; + case 'c': + case 'C': + sc = 0x2E; + break; + case 'd': + case 'D': + sc = 0x20; + break; + case 'e': + case 'E': + sc = 0x12; + break; + case 'f': + case 'F': + sc = 0x21; + break; + case 'g': + case 'G': + sc = 0x22; + break; + case 'h': + case 'H': + sc = 0x23; + break; + case 'i': + case 'I': + sc = 0x17; + break; + case 'j': + case 'J': + sc = 0x24; + break; + case 'k': + case 'K': + sc = 0x25; + break; + case 'l': + case 'L': + sc = 0x26; + break; + case 'm': + case 'M': + sc = 0x32; + break; + case 'n': + case 'N': + sc = 0x31; + break; + case 'o': + case 'O': + sc = 0x18; + break; + case 'p': + case 'P': + sc = 0x19; + break; + case 'q': + case 'Q': + sc = 0x10; + break; + case 'r': + case 'R': + sc = 0x13; + break; + case 's': + case 'S': + sc = 0x1F; + break; + case 't': + case 'T': + sc = 0x14; + break; + case 'u': + case 'U': + sc = 0x16; + break; + case 'v': + case 'V': + sc = 0x2F; + break; + case 'w': + case 'W': + sc = 0x11; + break; + case 'x': + case 'X': + sc = 0x2D; + break; + case 'y': + case 'Y': + sc = 0x15; + break; + case 'z': + case 'Z': + sc = 0x2C; + break; + + /* Russian Cyrillic letters - most common shortcuts */ + /* Lowercase Cyrillic */ + case 0x0444: + case 0x0424: + sc = 0x1E; + break; /* ф/Ф -> A */ + case 0x0438: + case 0x0418: + sc = 0x30; + break; /* и/И -> B */ + case 0x0441: + case 0x0421: + sc = 0x2E; + break; /* с/С -> C */ + case 0x0432: + case 0x0412: + sc = 0x20; + break; /* в/В -> D */ + case 0x0443: + case 0x0423: + sc = 0x12; + break; /* у/У -> E */ + case 0x0430: + case 0x0410: + sc = 0x21; + break; /* а/А -> F */ + case 0x043f: + case 0x041f: + sc = 0x22; + break; /* п/П -> G */ + case 0x0440: + case 0x0420: + sc = 0x23; + break; /* р/Р -> H */ + case 0x0448: + case 0x0428: + sc = 0x17; + break; /* ш/Ш -> I */ + case 0x043e: + case 0x041e: + sc = 0x24; + break; /* о/О -> J */ + case 0x043b: + case 0x041b: + sc = 0x25; + break; /* л/Л -> K */ + case 0x0434: + case 0x0414: + sc = 0x26; + break; /* д/Д -> L */ + case 0x044c: + case 0x042c: + sc = 0x32; + break; /* ь/Ь -> M */ + case 0x0442: + case 0x0422: + sc = 0x31; + break; /* т/Т -> N */ + case 0x0449: + case 0x0429: + sc = 0x18; + break; /* щ/Щ -> O */ + case 0x0437: + case 0x0417: + sc = 0x19; + break; /* з/З -> P */ + case 0x0439: + case 0x0419: + sc = 0x10; + break; /* й/Й -> Q */ + case 0x043a: + case 0x041a: + sc = 0x13; + break; /* к/К -> R */ + case 0x044b: + case 0x042b: + sc = 0x1F; + break; /* ы/Ы -> S */ + case 0x0435: + case 0x0415: + sc = 0x14; + break; /* е/Е -> T */ + case 0x0433: + case 0x0413: + sc = 0x16; + break; /* г/Г -> U */ + case 0x043c: + case 0x041c: + sc = 0x2F; + break; /* м/М -> V */ + case 0x0446: + case 0x0426: + sc = 0x11; + break; /* ц/Ц -> W */ + case 0x0447: + case 0x0427: + sc = 0x2D; + break; /* ч/Ч -> X */ + case 0x043d: + case 0x041d: + sc = 0x15; + break; /* н/Н -> Y */ + case 0x044f: + case 0x042f: + sc = 0x2C; + break; /* я/Я -> Z */ + + default: + sc = 0; + break; } - if (sc) { + if (sc) + { guac_client_log(client, GUAC_LOG_DEBUG, - "Sending keysym 0x%x as US scancode 0x%X due to modifiers", - keysym, sc); + "Sending keysym 0x%x as US scancode 0x%X due to modifiers", + keysym, sc); /* send press+release; 'flags' = 0 for regular keys */ guac_rdp_send_key_event(rdp_client, sc, 0, 1); guac_rdp_send_key_event(rdp_client, sc, 0, 0); @@ -789,7 +1108,8 @@ if (shortcut_mod) { return; guac_client_log(client, GUAC_LOG_DEBUG, "Sending keysym 0x%x as " - "Unicode", keysym); + "Unicode", + keysym); /* Translate keysym into codepoint */ int codepoint; @@ -797,36 +1117,39 @@ if (shortcut_mod) { codepoint = keysym; else if (keysym >= 0x1000000) codepoint = keysym & 0xFFFFFF; - else { + else + { guac_client_log(client, GUAC_LOG_DEBUG, "Unmapped keysym has no " - "equivalent unicode value: 0x%x", keysym); + "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, - unsigned int set_flags, unsigned int clear_flags) { +void guac_rdp_keyboard_update_locks(guac_rdp_keyboard *keyboard, + unsigned int set_flags, unsigned int clear_flags) +{ - guac_client* client = keyboard->client; - guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + guac_client *client = keyboard->client; + guac_rdp_client *rdp_client = (guac_rdp_client *)client->data; /* Calculate updated lock 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) { + if (lock_flags != keyboard->lock_flags) + { guac_rdp_send_synchronize_event(rdp_client, lock_flags); keyboard->lock_flags = lock_flags; } - } -void guac_rdp_keyboard_update_modifiers(guac_rdp_keyboard* keyboard, - unsigned int set_flags, unsigned int clear_flags) { +void guac_rdp_keyboard_update_modifiers(guac_rdp_keyboard *keyboard, + unsigned int set_flags, unsigned int clear_flags) +{ unsigned int modifier_flags = guac_rdp_keyboard_get_modifier_flags(keyboard); @@ -837,55 +1160,62 @@ void guac_rdp_keyboard_update_modifiers(guac_rdp_keyboard* keyboard, set_flags &= ~modifier_flags; /* 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, GUAC_RDP_KEYSYM_LSHIFT, 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, GUAC_RDP_KEYSYM_LSHIFT, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); guac_rdp_keyboard_update_keysym(keyboard, GUAC_RDP_KEYSYM_RSHIFT, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); } /* Press/release AltGr as needed */ - if (set_flags & GUAC_RDP_KEYMAP_MODIFIER_ALTGR) { + if (set_flags & GUAC_RDP_KEYMAP_MODIFIER_ALTGR) + { guac_rdp_keyboard_update_keysym(keyboard, GUAC_RDP_KEYSYM_ALTGR, 1, GUAC_RDP_KEY_SOURCE_SYNTHETIC); } - else if (clear_flags & GUAC_RDP_KEYMAP_MODIFIER_ALTGR) { + else if (clear_flags & GUAC_RDP_KEYMAP_MODIFIER_ALTGR) + { guac_rdp_keyboard_update_keysym(keyboard, GUAC_RDP_KEYSYM_ALTGR, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); guac_rdp_keyboard_update_keysym(keyboard, GUAC_RDP_KEYSYM_LALT, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); guac_rdp_keyboard_update_keysym(keyboard, GUAC_RDP_KEYSYM_RALT, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); guac_rdp_keyboard_update_keysym(keyboard, GUAC_RDP_KEYSYM_LCTRL, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); guac_rdp_keyboard_update_keysym(keyboard, GUAC_RDP_KEYSYM_RCTRL, 0, GUAC_RDP_KEY_SOURCE_SYNTHETIC); } - } -int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, - int keysym, int pressed, guac_rdp_key_source source) { +int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard *keyboard, + int keysym, int pressed, guac_rdp_key_source source) +{ /* Synchronize lock keys states, if this has not yet been done */ - if (!keyboard->synchronized) { + if (!keyboard->synchronized) + { - guac_client* client = keyboard->client; - guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + guac_client *client = keyboard->client; + guac_rdp_client *rdp_client = (guac_rdp_client *)client->data; /* Synchronize remote lock key states with local state */ guac_rdp_send_synchronize_event(rdp_client, keyboard->lock_flags); keyboard->synchronized = 1; - } - guac_rdp_key* key = guac_rdp_keyboard_get_key(keyboard, keysym); + guac_rdp_key *key = guac_rdp_keyboard_get_key(keyboard, keysym); /* Update tracking of client-side keyboard state but only for keys which * are tracked server-side, as well (to ensure that the key count remains * correct, even if a user sends extra unbalanced or excessive press and * release events) */ - if (source == GUAC_RDP_KEY_SOURCE_CLIENT && key != NULL) { - if (pressed && !key->user_pressed) { + if (source == GUAC_RDP_KEY_SOURCE_CLIENT && key != NULL) + { + if (pressed && !key->user_pressed) + { keyboard->user_pressed_keys++; key->user_pressed = 1; } - else if (!pressed && key->user_pressed) { + else if (!pressed && key->user_pressed) + { keyboard->user_pressed_keys--; key->user_pressed = 0; } @@ -893,7 +1223,8 @@ int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, /* Send events and update server-side lock state only if server-side key * state is changing (or if server-side state of this key is untracked) */ - if (key == NULL || (pressed && key->pressed == NULL) || (!pressed && key->pressed != NULL)) { + if (key == NULL || (pressed && key->pressed == NULL) || (!pressed && key->pressed != NULL)) + { /* Toggle locks on keydown */ if (pressed) @@ -901,8 +1232,9 @@ int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, /* 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) { + 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; } @@ -910,10 +1242,10 @@ int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, /* Fall back to dead keys or Unicode events if otherwise undefined inside * current keymap (note that we only handle "pressed" here, as neither * Unicode events nor dead keys can have a pressed/released state) */ - if (definition == NULL && pressed) { + if (definition == NULL && pressed) + { guac_rdp_keyboard_send_missing_key(keyboard, keysym); } - } /* Reset RDP server keyboard state (releasing any automatically @@ -923,30 +1255,31 @@ int guac_rdp_keyboard_update_keysym(guac_rdp_keyboard* keyboard, guac_rdp_keyboard_reset(keyboard); return 0; - } -void guac_rdp_keyboard_reset(guac_rdp_keyboard* keyboard) { +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]; + 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); + GUAC_RDP_KEY_SOURCE_SYNTHETIC); } - } -BOOL guac_rdp_keyboard_set_indicators(rdpContext* context, UINT16 flags) { +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; + guac_client *client = ((rdp_freerdp_context *)context)->client; + guac_rdp_client *rdp_client = (guac_rdp_client *)client->data; guac_rwlock_acquire_read_lock(&(rdp_client->lock)); /* Skip if keyboard not yet ready */ - guac_rdp_keyboard* keyboard = rdp_client->keyboard; + guac_rdp_keyboard *keyboard = rdp_client->keyboard; if (keyboard == NULL) goto complete; @@ -957,6 +1290,5 @@ BOOL guac_rdp_keyboard_set_indicators(rdpContext* context, UINT16 flags) { complete: guac_rwlock_release_lock(&(rdp_client->lock)); return TRUE; - }