@@ -306,28 +306,35 @@ Guacamole.OnScreenKeyboard = function(layout) {
306306 // Retrieve originally-pressed keysym, if modifier was already pressed
307307 var originalKeysym = modifierKeysyms [ key . modifier ] ;
308308
309- // Activate modifier if not pressed
310309 if ( originalKeysym === undefined ) {
311-
310+ // Activate modifier if not pressed
312311 addClass ( keyboard , modifierClass ) ;
313312 modifierKeysyms [ key . modifier ] = key . keysym ;
314-
315- // Send key event only if keysym is meaningful
316- if ( key . keysym && osk . onkeydown )
317- osk . onkeydown ( key . keysym ) ;
318-
319313 }
320-
321- // Deactivate if not pressed
322314 else {
323-
315+ // Deactivate if not pressed
324316 removeClass ( keyboard , modifierClass ) ;
325317 delete modifierKeysyms [ key . modifier ] ;
326-
327- // Send key event only if original keysym is meaningful
328- if ( originalKeysym && osk . onkeyup )
329- osk . onkeyup ( originalKeysym ) ;
318+ }
319+
320+ // Toggle modifiers send both keydown and keyup on each press
321+ if ( key . toggle ) {
322+ if ( key . keysym && osk . onkeydown )
323+ osk . onkeydown ( key . keysym ) ;
324+ if ( key . keysym && osk . onkeyup )
325+ osk . onkeyup ( key . keysym ) ;
326+ }
330327
328+ // Sticky modifiers send keydown on activate, keyup on deactivate
329+ else {
330+ if ( originalKeysym === undefined ) {
331+ if ( key . keysym && osk . onkeydown )
332+ osk . onkeydown ( key . keysym ) ;
333+ } // Deactivate if not pressed
334+ else {
335+ if ( originalKeysym && osk . onkeyup )
336+ osk . onkeyup ( originalKeysym ) ;
337+ }
331338 }
332339
333340 }
@@ -375,7 +382,7 @@ Guacamole.OnScreenKeyboard = function(layout) {
375382 }
376383
377384 } ;
378-
385+ // Deactivate if not pressed
379386 // Create keyboard
380387 var keyboard = document . createElement ( "div" ) ;
381388 keyboard . className = "guac-keyboard" ;
@@ -928,11 +935,22 @@ Guacamole.OnScreenKeyboard.Key = function(template, name) {
928935 * the names of keys; both the "RightShift" and "LeftShift" keys may set
929936 * the "shift" modifier, for example. By default, the key will affect no
930937 * modifiers.
931- *
938+ *
932939 * @type {string }
933940 */
934941 this . modifier = template . modifier ;
935942
943+ /**
944+ * Whether this key is a toggle modifier. Toggle modifiers send both
945+ * keydown and keyup events when pressed, so each press performs one
946+ * toggle of the modifier state (e.g., Caps Lock, Num Lock, Scroll Lock).
947+ * Non-toggle modifiers remain sticky and are released by a subsequent
948+ * press. By default, modifiers are not toggles.
949+ *
950+ * @type {boolean }
951+ */
952+ this . toggle = template . toggle || false ;
953+
936954 /**
937955 * An array containing the names of each modifier required for this key to
938956 * have an effect. For example, a lowercase letter may require nothing,
0 commit comments