You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@guacamole.apache.org by "DevOps9-debug (via GitHub)" <gi...@apache.org> on 2025/10/25 15:45:35 UTC

[PR] GUACAMOLE-2161: Fix Ctrl/Shift scancode issues for Unicode (RU/EN) layouts in RDP [guacamole-server]

DevOps9-debug opened a new pull request, #621:
URL: https://github.com/apache/guacamole-server/pull/621

   …youts in RDP.
   
   When using Guacamole with the Unicode keyboard layout (ru-ru or en-us), text input works fine but modifier keys (Ctrl, Shift, Alt) do not trigger correctly inside RDP sessions. This especially breaks shortcuts like Ctrl+C, Ctrl+V, and Ctrl+Z, as well as Shift-based capital input in Cyrillic mode.
   
   Root cause:
   - The existing keyboard.c scancode mapping and Unicode event handler ignored control key states when switching layouts.
   - Unicode events were sent without associated scancode synchronization, causing RDP to treat Ctrl/Shift as unpressed.
   
   Fix:
   - Updated keyboard.c to properly handle Ctrl, Alt, and Shift state transitions alongside Unicode key events.
   - Ensures Unicode layouts (English and Russian) transmit modifier combinations correctly to RDP servers.
   
   Tested on:
   - Guacamole 1.6.0 / 1.7.0 (patched build)
   - FreeRDP 2.11.5
   - Windows Server 2019 RDP and Windows 10 Pro RDP
   
   Verified results:
    Ctrl+C / Ctrl+V / Ctrl+Z now work in both English and Russian layouts.
    Shift-based capital input functions correctly.
    Layout switching (Alt+Shift) does not break input handling.
   
   This patch makes Guacamole fully usable in mixed-language (RU/EN) environments using Unicode keyboard input.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] GUACAMOLE-2161: Fix Ctrl/Shift scancode issues for Unicode (RU/EN) layouts in RDP [guacamole-server]

Posted by "corentin-soriano (via GitHub)" <gi...@apache.org>.
corentin-soriano commented on code in PR #621:
URL: https://github.com/apache/guacamole-server/pull/621#discussion_r2465218027


##########
src/protocols/rdp/keyboard.c:
##########
@@ -557,55 +560,596 @@ 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) {
-
-    guac_client* client = keyboard->client;
-    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+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;
+
+    /* 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)
+    {

Review Comment:
   Could we remove this `if` which seems to be the same as the block just above?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] GUACAMOLE-2161: Fix Ctrl/Shift scancode issues for Unicode (RU/EN) layouts in RDP [guacamole-server]

Posted by "DevOps9-debug (via GitHub)" <gi...@apache.org>.
DevOps9-debug commented on code in PR #621:
URL: https://github.com/apache/guacamole-server/pull/621#discussion_r2470601223


##########
src/protocols/rdp/keyboard.c:
##########
@@ -557,55 +560,596 @@ 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) {
-
-    guac_client* client = keyboard->client;
-    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+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;
+
+    /* 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)
+    {

Review Comment:
   Good question — the two if (ctrl_or_alt_pressed) blocks serve different purposes.
   The first handles Unicode-layout-specific corrections (Cyrillic → Latin normalization and Ctrl + C/V fixes), while this second block maps regular ASCII letters/digits to US scancodes so that generic shortcuts like Ctrl + A, Ctrl + Z, etc. function correctly under Unicode mode.
   I’ve added a clarifying comment in the code to make that separation clear.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] GUACAMOLE-2161: Fix Ctrl/Shift scancode issues for Unicode (RU/EN) layouts in RDP [guacamole-server]

Posted by "DevOps9-debug (via GitHub)" <gi...@apache.org>.
DevOps9-debug commented on code in PR #621:
URL: https://github.com/apache/guacamole-server/pull/621#discussion_r2463272217


##########
src/protocols/rdp/keyboard.c:
##########
@@ -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; // Я
+        }
+    }

Review Comment:
   To clarify — this logic intentionally lives in keyboard.c because it only executes when Guacamole is operating in Unicode input mode. In that mode, the normal keymap layer (keymaps/*.h) is completely bypassed, and Guacamole sends Unicode events directly to the RDP server. Without an explicit mapping here, modifier combinations like Ctrl+C / Ctrl+V (and Cyrillic characters typed via Unicode) fail to produce the expected scancodes on the RDP side.
   
   So this section effectively acts as a bridge between Unicode input and scancode-based shortcuts, ensuring consistent behavior across both English and Cyrillic layouts.
   
   Regarding arithmetic mapping — unfortunately, Cyrillic Unicode codepoints (0x0410–0x044F) are not linearly aligned with their Latin counterparts, so a formulaic conversion would produce incorrect results for several keys. A switch remains the safest and most predictable approach here.
   
   I’ve updated the formatting to match Guacamole’s style guidelines (4-space indentation, each case on its own line) and added comments to clarify why this logic is inline rather than in the keymap.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] GUACAMOLE-2161: Fix Ctrl/Shift scancode issues for Unicode (RU/EN) layouts in RDP [guacamole-server]

Posted by "necouchman (via GitHub)" <gi...@apache.org>.
necouchman commented on code in PR #621:
URL: https://github.com/apache/guacamole-server/pull/621#discussion_r2464179606


##########
src/protocols/rdp/keyboard.c:
##########
@@ -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; // Я
+        }
+    }

Review Comment:
   Thanks for the clarification, this is helpful. The thing I'm still not sure about - and I'd like to have some of the other developers' opinions on - is whether we should be placing language-specific code (in this case, the Cyrillic characters) into the main body of code like this. I understand why you're doing it, I'm just not sure whether there's a better place for it or we should create some sort of other Unicode translation layer/system to handle this that could eventually be expanded to include any other languages/keymaps where this type of translation needs to take place.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] GUACAMOLE-2161: Fix Ctrl/Shift scancode issues for Unicode (RU/EN) layouts in RDP [guacamole-server]

Posted by "DevOps9-debug (via GitHub)" <gi...@apache.org>.
DevOps9-debug commented on code in PR #621:
URL: https://github.com/apache/guacamole-server/pull/621#discussion_r2463269387


##########
src/protocols/rdp/keyboard.c:
##########
@@ -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;
+    }
+}

Review Comment:
   done,



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] GUACAMOLE-2161: Fix Ctrl/Shift scancode issues for Unicode (RU/EN) layouts in RDP [guacamole-server]

Posted by "necouchman (via GitHub)" <gi...@apache.org>.
necouchman commented on code in PR #621:
URL: https://github.com/apache/guacamole-server/pull/621#discussion_r2462923386


##########
src/protocols/rdp/keyboard.c:
##########
@@ -27,7 +27,6 @@
 #include <guacamole/client.h>
 #include <guacamole/mem.h>
 #include <guacamole/rwlock.h>
-

Review Comment:
   This line can stay.



##########
src/protocols/rdp/keyboard.c:
##########
@@ -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;
+    }
+}

Review Comment:
   Please fix indentation in this section to match with the style maintained throughout the rest of the code.



##########
src/protocols/rdp/keyboard.c:
##########
@@ -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':

Review Comment:
   These should be on separate lines.



##########
src/protocols/rdp/keyboard.c:
##########
@@ -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':

Review Comment:
   These should be on separate lines.



##########
src/protocols/rdp/keyboard.c:
##########
@@ -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; // Я
+        }
+    }

Review Comment:
   A few issues, here:
   * I'm not sure about maintaining keymap translations directly in the code like this - this is one of the things that I believe the individual keymaps are supposed to handle. Is there some reason this has to be done in the code?
   * Is there any way to do this without the `switch` statement, like maybe some math that takes in the values for the `case` statements and automatically converts it to the expected keysym?
   * Please match style as maintained throughout the rest of the code - putting the entirety of each `case` statement on a single line makes for more difficult readability.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] GUACAMOLE-2161: Fix Ctrl/Shift scancode issues for Unicode (RU/EN) layouts in RDP [guacamole-server]

Posted by "DevOps9-debug (via GitHub)" <gi...@apache.org>.
DevOps9-debug commented on PR #621:
URL: https://github.com/apache/guacamole-server/pull/621#issuecomment-3446876878

   Thanks for the feedback! I’ll fix the indentation and separate the case statements.
   Regarding the Cyrillic mapping: this logic handles Unicode layouts where scancodes aren’t used, so .ksy mappings aren’t applied — but I’ll explore moving it to a more maintainable structure.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] GUACAMOLE-2161: Fix Ctrl/Shift scancode issues for Unicode (RU/EN) layouts in RDP [guacamole-server]

Posted by "necouchman (via GitHub)" <gi...@apache.org>.
necouchman commented on PR #621:
URL: https://github.com/apache/guacamole-server/pull/621#issuecomment-3699612208

   > There are many style changes in this pull request (moving braces, pointer asterisks, changes to indentation, and line breaks) but I don't see any reason to change the existing coding style here.
   > 
   
   Agreed, the code style should match existing conventions and should not be re-formatting the way we've laid out the code. The style has 1) been chosen specifically by the creators of the project, and 2) is relatively consistent throughout the code.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org