Skip to content

Commit a301739

Browse files
authored
GUACAMOLE-2207: Merge correction to keyboard handling allowing Cmd-click (Meta-click).
2 parents d9dda53 + 6e8f839 commit a301739

3 files changed

Lines changed: 82 additions & 5 deletions

File tree

guacamole-common-js/src/main/webapp/modules/Keyboard.js

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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)

guacamole/src/main/frontend/src/app/client/directives/guacClient.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,11 @@ angular.module('client').directive('guacClient', [function guacClient() {
243243
if (!client || !display)
244244
return;
245245

246+
// Broadcast mousedown event before sending to allow keyboard to resolve
247+
// deferred modifier keys (especially CMD+click)
248+
if (event.type === 'mousedown')
249+
$rootScope.$broadcast('guacBeforeClientMouseDown', event, client);
250+
246251
event.stopPropagation();
247252
event.preventDefault();
248253

@@ -303,6 +308,11 @@ angular.module('client').directive('guacClient', [function guacClient() {
303308
if (!client || !display)
304309
return;
305310

311+
// Broadcast mousedown event before sending to allow keyboard to resolve
312+
// deferred modifier keys (especially CMD+click)
313+
if (event.type === 'mousedown')
314+
$rootScope.$broadcast('guacBeforeClientMouseDown', event, client);
315+
306316
event.stopPropagation();
307317
event.preventDefault();
308318

guacamole/src/main/frontend/src/app/index/controllers/indexController.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,9 @@ angular.module('index').controller('indexController', ['$scope', '$injector',
183183
var keyboard = new Guacamole.Keyboard($document[0]);
184184
keyboard.listenTo(sink.getElement());
185185

186-
// Update keyboard modifiers based mouse click events
187-
$scope.$on('guacClientMouseDown', function mouseDownModifierSync(event, mouseEvent) {
186+
// Update keyboard modifiers based mouse click events. Note we need to run
187+
// before mousedown to resolve deferred Meta key events to enable Cmd+Click.
188+
$scope.$on('guacBeforeClientMouseDown', function mouseDownModifierSync(event, mouseEvent) {
188189
keyboard.updateModifiersFromMouse(mouseEvent);
189190
});
190191

0 commit comments

Comments
 (0)