From ffe3089637af2485322c727c8b9319e8c1085933 Mon Sep 17 00:00:00 2001 From: Bradley Bennett Date: Tue, 24 Feb 2026 17:02:45 -0500 Subject: [PATCH] GUACAMOLE-2232: On-Screen Keyboard: CapsLock gets out-of-sync on macOS. --- .../main/webapp/modules/OnScreenKeyboard.js | 50 +++++++++++++------ .../frontend/src/layouts/de-de-qwertz.json | 1 + .../frontend/src/layouts/en-us-qwerty.json | 1 + .../frontend/src/layouts/es-es-qwerty.json | 1 + .../frontend/src/layouts/fr-fr-azerty.json | 1 + .../frontend/src/layouts/it-it-qwerty.json | 1 + .../frontend/src/layouts/nl-nl-qwerty.json | 1 + .../frontend/src/layouts/ru-ru-qwerty.json | 1 + .../frontend/src/layouts/tr-tr-qwerty.json | 1 + 9 files changed, 42 insertions(+), 16 deletions(-) diff --git a/guacamole-common-js/src/main/webapp/modules/OnScreenKeyboard.js b/guacamole-common-js/src/main/webapp/modules/OnScreenKeyboard.js index 221f3b2b37..1a7700720a 100644 --- a/guacamole-common-js/src/main/webapp/modules/OnScreenKeyboard.js +++ b/guacamole-common-js/src/main/webapp/modules/OnScreenKeyboard.js @@ -306,28 +306,35 @@ Guacamole.OnScreenKeyboard = function(layout) { // Retrieve originally-pressed keysym, if modifier was already pressed var originalKeysym = modifierKeysyms[key.modifier]; - // Activate modifier if not pressed if (originalKeysym === undefined) { - + // Activate modifier if not pressed addClass(keyboard, modifierClass); modifierKeysyms[key.modifier] = key.keysym; - - // Send key event only if keysym is meaningful - if (key.keysym && osk.onkeydown) - osk.onkeydown(key.keysym); - } - - // Deactivate if not pressed else { - + // Deactivate if not pressed removeClass(keyboard, modifierClass); delete modifierKeysyms[key.modifier]; - - // Send key event only if original keysym is meaningful - if (originalKeysym && osk.onkeyup) - osk.onkeyup(originalKeysym); + } + + // Toggle modifiers send both keydown and keyup on each press + if (key.toggle) { + if (key.keysym && osk.onkeydown) + osk.onkeydown(key.keysym); + if (key.keysym && osk.onkeyup) + osk.onkeyup(key.keysym); + } + // Sticky modifiers send keydown on activate, keyup on deactivate + else { + if (originalKeysym === undefined) { + if (key.keysym && osk.onkeydown) + osk.onkeydown(key.keysym); + }// Deactivate if not pressed + else { + if (originalKeysym && osk.onkeyup) + osk.onkeyup(originalKeysym); + } } } @@ -375,7 +382,7 @@ Guacamole.OnScreenKeyboard = function(layout) { } }; - +// Deactivate if not pressed // Create keyboard var keyboard = document.createElement("div"); keyboard.className = "guac-keyboard"; @@ -928,11 +935,22 @@ Guacamole.OnScreenKeyboard.Key = function(template, name) { * the names of keys; both the "RightShift" and "LeftShift" keys may set * the "shift" modifier, for example. By default, the key will affect no * modifiers. - * + * * @type {string} */ this.modifier = template.modifier; + /** + * Whether this key is a toggle modifier. Toggle modifiers send both + * keydown and keyup events when pressed, so each press performs one + * toggle of the modifier state (e.g., Caps Lock, Num Lock, Scroll Lock). + * Non-toggle modifiers remain sticky and are released by a subsequent + * press. By default, modifiers are not toggles. + * + * @type {boolean} + */ + this.toggle = template.toggle || false; + /** * An array containing the names of each modifier required for this key to * have an effect. For example, a lowercase letter may require nothing, diff --git a/guacamole/src/main/frontend/src/layouts/de-de-qwertz.json b/guacamole/src/main/frontend/src/layouts/de-de-qwertz.json index 052a3a7e0c..67403d3034 100644 --- a/guacamole/src/main/frontend/src/layouts/de-de-qwertz.json +++ b/guacamole/src/main/frontend/src/layouts/de-de-qwertz.json @@ -103,6 +103,7 @@ "Caps" : [{ "title" : "Caps", "modifier" : "caps", + "toggle" : true, "keysym" : 65509 }], "LAlt" : [{ diff --git a/guacamole/src/main/frontend/src/layouts/en-us-qwerty.json b/guacamole/src/main/frontend/src/layouts/en-us-qwerty.json index fa9a6fe680..5505666c22 100644 --- a/guacamole/src/main/frontend/src/layouts/en-us-qwerty.json +++ b/guacamole/src/main/frontend/src/layouts/en-us-qwerty.json @@ -75,6 +75,7 @@ "Caps" : [{ "title" : "Caps", "modifier" : "caps", + "toggle" : true, "keysym" : 65509 }], "LAlt" : [{ diff --git a/guacamole/src/main/frontend/src/layouts/es-es-qwerty.json b/guacamole/src/main/frontend/src/layouts/es-es-qwerty.json index e88c6cda47..1fd80e5302 100644 --- a/guacamole/src/main/frontend/src/layouts/es-es-qwerty.json +++ b/guacamole/src/main/frontend/src/layouts/es-es-qwerty.json @@ -103,6 +103,7 @@ "Caps" : [{ "title" : "Caps", "modifier" : "caps", + "toggle" : true, "keysym" : 65509 }], "LAlt" : [{ diff --git a/guacamole/src/main/frontend/src/layouts/fr-fr-azerty.json b/guacamole/src/main/frontend/src/layouts/fr-fr-azerty.json index bf3eaf3c90..f8935dfcef 100644 --- a/guacamole/src/main/frontend/src/layouts/fr-fr-azerty.json +++ b/guacamole/src/main/frontend/src/layouts/fr-fr-azerty.json @@ -106,6 +106,7 @@ "Caps" : [{ "title" : "Caps", "modifier" : "caps", + "toggle" : true, "keysym" : 65509 }], "LAlt" : [{ diff --git a/guacamole/src/main/frontend/src/layouts/it-it-qwerty.json b/guacamole/src/main/frontend/src/layouts/it-it-qwerty.json index 4775e6fd6c..8fc2f98902 100644 --- a/guacamole/src/main/frontend/src/layouts/it-it-qwerty.json +++ b/guacamole/src/main/frontend/src/layouts/it-it-qwerty.json @@ -103,6 +103,7 @@ "Caps" : [{ "title" : "Caps", "modifier" : "caps", + "toggle" : true, "keysym" : 65509 }], "LAlt" : [{ diff --git a/guacamole/src/main/frontend/src/layouts/nl-nl-qwerty.json b/guacamole/src/main/frontend/src/layouts/nl-nl-qwerty.json index f1e8744776..78c3ce23d3 100644 --- a/guacamole/src/main/frontend/src/layouts/nl-nl-qwerty.json +++ b/guacamole/src/main/frontend/src/layouts/nl-nl-qwerty.json @@ -103,6 +103,7 @@ "Caps" : [{ "title" : "Caps", "modifier" : "caps", + "toggle" : true, "keysym" : 65509 }], "LAlt" : [{ diff --git a/guacamole/src/main/frontend/src/layouts/ru-ru-qwerty.json b/guacamole/src/main/frontend/src/layouts/ru-ru-qwerty.json index 63fa1cfa4c..61a0f6edf8 100644 --- a/guacamole/src/main/frontend/src/layouts/ru-ru-qwerty.json +++ b/guacamole/src/main/frontend/src/layouts/ru-ru-qwerty.json @@ -75,6 +75,7 @@ "Caps" : [{ "title" : "Caps", "modifier" : "caps", + "toggle" : true, "keysym" : 65509 }], "LAlt" : [{ diff --git a/guacamole/src/main/frontend/src/layouts/tr-tr-qwerty.json b/guacamole/src/main/frontend/src/layouts/tr-tr-qwerty.json index d126d1d472..b0838886ea 100644 --- a/guacamole/src/main/frontend/src/layouts/tr-tr-qwerty.json +++ b/guacamole/src/main/frontend/src/layouts/tr-tr-qwerty.json @@ -74,6 +74,7 @@ "Caps" : [{ "title" : "Caps", "modifier" : "caps", + "toggle" : true, "keysym" : 65509 }], "LAlt" : [{