From eaed778f910eeee337d84d6f216285c63dca046d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 20 Dec 2014 14:05:54 -0800 Subject: [PATCH] GUAC-810: Send "synthetic" key events for non-physical input options. Allow components to prevent production/handling of guacKeyup/guacKeydown via guacBeforeKeyup/guacBeforeKeydown. --- .../app/client/directives/guacClient.js | 10 ++++ .../app/index/controllers/indexController.js | 22 ++++++-- .../main/webapp/app/osk/directives/guacOsk.js | 4 +- .../app/textInput/directives/guacKey.js | 8 +-- .../app/textInput/directives/guacTextInput.js | 52 ++++++++++++++++++- 5 files changed, 85 insertions(+), 11 deletions(-) diff --git a/guacamole/src/main/webapp/app/client/directives/guacClient.js b/guacamole/src/main/webapp/app/client/directives/guacClient.js index 476c5ae11..c5f7bcd35 100644 --- a/guacamole/src/main/webapp/app/client/directives/guacClient.js +++ b/guacamole/src/main/webapp/app/client/directives/guacClient.js @@ -529,6 +529,16 @@ angular.module('client').directive('guacClient', [function guacClient() { event.preventDefault(); } }); + + // Universally handle all synthetic keydown events + $scope.$on('guacSyntheticKeydown', function syntheticKeydownListener(event, keysym) { + client.sendKeyEvent(1, keysym); + }); + + // Universally handle all synthetic keyup events + $scope.$on('guacSyntheticKeyup', function syntheticKeyupListener(event, keysym) { + client.sendKeyEvent(0, keysym); + }); /** * Converts the given bytes to a base64-encoded string. diff --git a/guacamole/src/main/webapp/app/index/controllers/indexController.js b/guacamole/src/main/webapp/app/index/controllers/indexController.js index 25ca651b6..c927e22e2 100644 --- a/guacamole/src/main/webapp/app/index/controllers/indexController.js +++ b/guacamole/src/main/webapp/app/index/controllers/indexController.js @@ -193,19 +193,35 @@ angular.module('index').controller('indexController', ['$scope', '$injector', // Try to load them now $scope.loadBasicPermissions(); - + // Create event listeners at the global level var keyboard = new Guacamole.Keyboard($document[0]); - // Broadcast keydown events down the scope heirarchy + // Broadcast keydown events keyboard.onkeydown = function onkeydown(keysym) { + + // Warn of pending keydown + var guacBeforeKeydownEvent = $scope.$broadcast('guacBeforeKeydown', keysym, keyboard); + if (guacBeforeKeydownEvent.defaultPrevented) + return true; + + // If not prevented via guacBeforeKeydown, fire corresponding keydown event var guacKeydownEvent = $scope.$broadcast('guacKeydown', keysym, keyboard); return !guacKeydownEvent.defaultPrevented; + }; - // Broadcast keyup events down the scope heirarchy + // Broadcast keyup events keyboard.onkeyup = function onkeyup(keysym) { + + // Warn of pending keyup + var guacBeforeKeydownEvent = $scope.$broadcast('guacBeforeKeyup', keysym, keyboard); + if (guacBeforeKeydownEvent.defaultPrevented) + return; + + // If not prevented via guacBeforeKeyup, fire corresponding keydown event $scope.$broadcast('guacKeyup', keysym, keyboard); + }; // Release all keys when window loses focus diff --git a/guacamole/src/main/webapp/app/osk/directives/guacOsk.js b/guacamole/src/main/webapp/app/osk/directives/guacOsk.js index 6b7cf529c..f8891e437 100644 --- a/guacamole/src/main/webapp/app/osk/directives/guacOsk.js +++ b/guacamole/src/main/webapp/app/osk/directives/guacOsk.js @@ -88,12 +88,12 @@ angular.module('osk').directive('guacOsk', [function guacOsk() { // Broadcast keydown for each key pressed keyboard.onkeydown = function(keysym) { - $rootScope.$broadcast('guacKeydown', keysym); + $rootScope.$broadcast('guacSyntheticKeydown', keysym); }; // Broadcast keydown for each key released keyboard.onkeyup = function(keysym) { - $rootScope.$broadcast('guacKeyup', keysym); + $rootScope.$broadcast('guacSyntheticKeyup', keysym); }; // Resize keyboard whenever window changes size diff --git a/guacamole/src/main/webapp/app/textInput/directives/guacKey.js b/guacamole/src/main/webapp/app/textInput/directives/guacKey.js index c30665625..f6dd6f41f 100644 --- a/guacamole/src/main/webapp/app/textInput/directives/guacKey.js +++ b/guacamole/src/main/webapp/app/textInput/directives/guacKey.js @@ -90,8 +90,8 @@ angular.module('textInput').directive('guacKey', [function guacKey() { // For all non-sticky keys, press and release key immediately else { - $rootScope.$broadcast('guacKeydown', $scope.keysym); - $rootScope.$broadcast('guacKeyup', $scope.keysym); + $rootScope.$broadcast('guacSyntheticKeydown', $scope.keysym); + $rootScope.$broadcast('guacSyntheticKeyup', $scope.keysym); } }; @@ -101,11 +101,11 @@ angular.module('textInput').directive('guacKey', [function guacKey() { // If the key is pressed now, send keydown if (isPressed) - $rootScope.$broadcast('guacKeydown', $scope.keysym); + $rootScope.$broadcast('guacSyntheticKeydown', $scope.keysym); // If the key was pressed, but is not pressed any longer, send keyup else if (wasPressed) - $rootScope.$broadcast('guacKeyup', $scope.keysym); + $rootScope.$broadcast('guacSyntheticKeyup', $scope.keysym); }); diff --git a/guacamole/src/main/webapp/app/textInput/directives/guacTextInput.js b/guacamole/src/main/webapp/app/textInput/directives/guacTextInput.js index c09572489..729996dbf 100644 --- a/guacamole/src/main/webapp/app/textInput/directives/guacTextInput.js +++ b/guacamole/src/main/webapp/app/textInput/directives/guacTextInput.js @@ -50,6 +50,42 @@ angular.module('textInput').directive('guacTextInput', [function guacTextInput() */ var TEXT_INPUT_PADDING_CODEPOINT = 0x200B; + /** + * Keys which should be allowed through to the client when in text + * input mode, providing corresponding key events are received. + * Keys in this set will be allowed through to the server. + * + * @type Object. + */ + var ALLOWED_KEYS = { + 0xFF08: true, /* Backspace */ + 0xFF09: true, /* Tab */ + 0xFF0D: true, /* Enter */ + 0xFF1B: true, /* Escape */ + 0xFF50: true, /* Home */ + 0xFF51: true, /* Left */ + 0xFF52: true, /* Up */ + 0xFF53: true, /* Right */ + 0xFF54: true, /* Down */ + 0xFF57: true, /* End */ + 0xFF64: true, /* Insert */ + 0xFFBE: true, /* F1 */ + 0xFFBF: true, /* F2 */ + 0xFFC0: true, /* F3 */ + 0xFFC1: true, /* F4 */ + 0xFFC2: true, /* F5 */ + 0xFFC3: true, /* F6 */ + 0xFFC4: true, /* F7 */ + 0xFFC5: true, /* F8 */ + 0xFFC6: true, /* F9 */ + 0xFFC7: true, /* F10 */ + 0xFFC8: true, /* F11 */ + 0xFFC9: true, /* F12 */ + 0xFFE1: true, /* Left shift */ + 0xFFE2: true, /* Right shift */ + 0xFFFF: true /* Delete */ + }; + /** * Recently-sent text, ordered from oldest to most recent. * @@ -151,8 +187,8 @@ angular.module('textInput').directive('guacTextInput', [function guacTextInput() * @param {Number} keysym The keysym of the key to send. */ var sendKeysym = function sendKeysym(keysym) { - $rootScope.$broadcast('guacKeydown', keysym); - $rootScope.$broadcast('guacKeyup', keysym); + $rootScope.$broadcast('guacSyntheticKeydown', keysym); + $rootScope.$broadcast('guacSyntheticKeyup', keysym); }; /** @@ -283,6 +319,18 @@ angular.module('textInput').directive('guacTextInput', [function guacTextInput() e.preventDefault(); }, false); + // If the text input UI has focus, prevent keydown events + $scope.$on('guacBeforeKeydown', function filterKeydown(event, keysym) { + if (hasFocus && !ALLOWED_KEYS[keysym]) + event.preventDefault(); + }); + + // If the text input UI has focus, prevent keyup events + $scope.$on('guacBeforeKeyup', function filterKeyup(event, keysym) { + if (hasFocus && !ALLOWED_KEYS[keysym]) + event.preventDefault(); + }); + }] };