@@ -294,8 +294,10 @@ Guacamole.Keyboard = function Keyboard(element) {
294294 this . keysym = keysym_from_key_identifier ( this . keyIdentifier , this . location , this . modifiers . shift ) ;
295295
296296 // If a key is pressed while meta is held down, the keyup will
297- // never be sent in Chrome (bug #108404)
298- if ( this . modifiers . meta && this . keysym !== 0xFFE7 && this . keysym !== 0xFFE8 )
297+ // never be sent in Chrome (bug #108404). Modifier keys are excluded
298+ // from this workaround as they have reliable keyup events and need
299+ // to be held down simultaneously with Meta.
300+ if ( this . modifiers . meta && ! isModifierKey ( this . keysym ) )
299301 this . keyupReliable = false ;
300302
301303 // We cannot rely on receiving keyup for lock keys on certain platforms
@@ -593,6 +595,23 @@ Guacamole.Keyboard = function Keyboard(element) {
593595 hyper : [ 0xFFEB , 0xFFEC ] // Left super/hyper, Right super/hyper
594596 } ;
595597
598+ /**
599+ * All modifier key keysyms for easy lookup.
600+ *
601+ * @private
602+ * @type {!Object.<number, boolean> }
603+ */
604+ var modifierKeysyms = ( function ( ) {
605+ var lookup = { } ;
606+ for ( var modifier in modifierKeysymsByType ) {
607+ var keysyms = modifierKeysymsByType [ modifier ] ;
608+ for ( var i = 0 ; i < keysyms . length ; i ++ ) {
609+ lookup [ keysyms [ i ] ] = true ;
610+ }
611+ }
612+ return lookup ;
613+ } ) ( ) ;
614+
596615 /**
597616 * All keysyms that represent each supported toggle modifier
598617 * type.
@@ -801,6 +820,36 @@ Guacamole.Keyboard = function Keyboard(element) {
801820 || isScrollLockKey ( keysym ) ;
802821 } ;
803822
823+ /**
824+ * Returns true if the given keysym corresponds to a Meta key (left or
825+ * right Meta/Command/Windows key).
826+ *
827+ * @private
828+ * @param {!number } keysym
829+ * The keysym to check.
830+ *
831+ * @returns {!boolean }
832+ * true if the given keysym corresponds to a Meta key, false otherwise.
833+ */
834+ var isMetaKey = function isMetaKey ( keysym ) {
835+ return modifierKeysymsByType . meta . indexOf ( keysym ) !== - 1 ;
836+ } ;
837+
838+ /**
839+ * Returns true if the given keysym corresponds to a modifier key
840+ * (Shift, Ctrl, Alt, Meta, Hyper, AltGr).
841+ *
842+ * @private
843+ * @param {!number } keysym
844+ * The keysym to check.
845+ *
846+ * @returns {!boolean }
847+ * true if the given keysym corresponds to a modifier key, false otherwise.
848+ */
849+ var isModifierKey = function isModifierKey ( keysym ) {
850+ return modifierKeysyms [ keysym ] === true ;
851+ } ;
852+
804853 function keysym_from_key_identifier ( identifier , location , shifted ) {
805854
806855 if ( ! identifier )
@@ -1060,6 +1109,23 @@ Guacamole.Keyboard = function Keyboard(element) {
10601109 // Keep lock modifiers synchronized using mouse event modifier flags
10611110 syncToggleModifierStates ( mouseEvent . modifiers ) ;
10621111
1112+ // Check if there's a pending Meta key waiting for context
1113+ var hasPendingMeta = eventLog . length > 0 &&
1114+ eventLog [ 0 ] instanceof KeydownEvent &&
1115+ isMetaKey ( eventLog [ 0 ] . keysym ) ;
1116+
1117+ // Only add mouse event if it has meta modifier and there's a pending Meta key
1118+ if ( mouseEvent . modifiers . meta && hasPendingMeta ) {
1119+ // Push mouse event onto the event log to provide context for the
1120+ // deferred Meta key. The mouse event will be silently dropped when
1121+ // processed as it's not a KeyEvent type.
1122+ eventLog . push ( mouseEvent ) ;
1123+
1124+ // Process the event log, which will now resolve the deferred Meta
1125+ // key using the mouse event's modifier state as context
1126+ interpret_events ( ) ;
1127+ }
1128+
10631129 } ;
10641130
10651131 /**
@@ -1384,7 +1450,7 @@ Guacamole.Keyboard = function Keyboard(element) {
13841450 // Defer handling of Meta until it is known to be functioning as a
13851451 // modifier (it may otherwise actually be an alternative method for
13861452 // pressing a single key, such as Meta+Left for Home on ChromeOS)
1387- if ( first . keysym === 0xFFE7 || first . keysym === 0xFFE8 ) {
1453+ if ( isMetaKey ( first . keysym ) ) {
13881454
13891455 // Defer handling until further events exist to provide context
13901456 if ( eventLog . length === 1 )
0 commit comments