/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var Guacamole = Guacamole || {};
/**
* An object that will accept raw key events and produce a chronologically
* ordered array of key event objects. These events can be obtained by
* calling getEvents().
*
* @constructor
* @param {number} [startTimestamp=0]
* The starting timestamp for the recording being intepreted. If provided,
* the timestamp of each intepreted event will be relative to this timestamp.
* If not provided, the raw recording timestamp will be used.
*/
Guacamole.KeyEventInterpreter = function KeyEventInterpreter(startTimestamp) {
// Default to 0 seconds to keep the raw timestamps
if (startTimestamp === undefined || startTimestamp === null)
startTimestamp = 0;
/**
* A precursor array to the KNOWN_KEYS map. The objects contained within
* will be constructed into full KeyDefinition objects.
*
* @constant
* @private
* @type {Object[]}
*/
var _KNOWN_KEYS = [
{keysym: 0xFE03, name: 'AltGr' },
{keysym: 0xFF08, name: 'Backspace' },
{keysym: 0xFF09, name: 'Tab' },
{keysym: 0xFF0B, name: 'Clear' },
{keysym: 0xFF0D, name: 'Return', value: "\n" },
{keysym: 0xFF13, name: 'Pause' },
{keysym: 0xFF14, name: 'Scroll' },
{keysym: 0xFF15, name: 'SysReq' },
{keysym: 0xFF1B, name: 'Escape' },
{keysym: 0xFF50, name: 'Home' },
{keysym: 0xFF51, name: 'Left' },
{keysym: 0xFF52, name: 'Up' },
{keysym: 0xFF53, name: 'Right' },
{keysym: 0xFF54, name: 'Down' },
{keysym: 0xFF55, name: 'Page Up' },
{keysym: 0xFF56, name: 'Page Down' },
{keysym: 0xFF57, name: 'End' },
{keysym: 0xFF63, name: 'Insert' },
{keysym: 0xFF65, name: 'Undo' },
{keysym: 0xFF6A, name: 'Help' },
{keysym: 0xFF7F, name: 'Num' },
{keysym: 0xFF80, name: 'Space', value: " " },
{keysym: 0xFF8D, name: 'Enter', value: "\n" },
{keysym: 0xFF95, name: 'Home' },
{keysym: 0xFF96, name: 'Left' },
{keysym: 0xFF97, name: 'Up' },
{keysym: 0xFF98, name: 'Right' },
{keysym: 0xFF99, name: 'Down' },
{keysym: 0xFF9A, name: 'Page Up' },
{keysym: 0xFF9B, name: 'Page Down' },
{keysym: 0xFF9C, name: 'End' },
{keysym: 0xFF9E, name: 'Insert' },
{keysym: 0xFFAA, name: '*', value: "*" },
{keysym: 0xFFAB, name: '+', value: "+" },
{keysym: 0xFFAD, name: '-', value: "-" },
{keysym: 0xFFAE, name: '.', value: "." },
{keysym: 0xFFAF, name: '/', value: "/" },
{keysym: 0xFFB0, name: '0', value: "0" },
{keysym: 0xFFB1, name: '1', value: "1" },
{keysym: 0xFFB2, name: '2', value: "2" },
{keysym: 0xFFB3, name: '3', value: "3" },
{keysym: 0xFFB4, name: '4', value: "4" },
{keysym: 0xFFB5, name: '5', value: "5" },
{keysym: 0xFFB6, name: '6', value: "6" },
{keysym: 0xFFB7, name: '7', value: "7" },
{keysym: 0xFFB8, name: '8', value: "8" },
{keysym: 0xFFB9, name: '9', value: "9" },
{keysym: 0xFFBE, name: 'F1' },
{keysym: 0xFFBF, name: 'F2' },
{keysym: 0xFFC0, name: 'F3' },
{keysym: 0xFFC1, name: 'F4' },
{keysym: 0xFFC2, name: 'F5' },
{keysym: 0xFFC3, name: 'F6' },
{keysym: 0xFFC4, name: 'F7' },
{keysym: 0xFFC5, name: 'F8' },
{keysym: 0xFFC6, name: 'F9' },
{keysym: 0xFFC7, name: 'F10' },
{keysym: 0xFFC8, name: 'F11' },
{keysym: 0xFFC9, name: 'F12' },
{keysym: 0xFFCA, name: 'F13' },
{keysym: 0xFFCB, name: 'F14' },
{keysym: 0xFFCC, name: 'F15' },
{keysym: 0xFFCD, name: 'F16' },
{keysym: 0xFFCE, name: 'F17' },
{keysym: 0xFFCF, name: 'F18' },
{keysym: 0xFFD0, name: 'F19' },
{keysym: 0xFFD1, name: 'F20' },
{keysym: 0xFFD2, name: 'F21' },
{keysym: 0xFFD3, name: 'F22' },
{keysym: 0xFFD4, name: 'F23' },
{keysym: 0xFFD5, name: 'F24' },
{keysym: 0xFFE1, name: 'Shift' },
{keysym: 0xFFE2, name: 'Shift' },
{keysym: 0xFFE3, name: 'Ctrl' },
{keysym: 0xFFE4, name: 'Ctrl' },
{keysym: 0xFFE5, name: 'Caps' },
{keysym: 0xFFE7, name: 'Meta' },
{keysym: 0xFFE8, name: 'Meta' },
{keysym: 0xFFE9, name: 'Alt' },
{keysym: 0xFFEA, name: 'Alt' },
{keysym: 0xFFEB, name: 'Super' },
{keysym: 0xFFEC, name: 'Super' },
{keysym: 0xFFED, name: 'Hyper' },
{keysym: 0xFFEE, name: 'Hyper' },
{keysym: 0xFFFF, name: 'Delete' }
];
/**
* All known keys, as a map of X11 keysym to KeyDefinition.
*
* @constant
* @private
* @type {Object.<String, KeyDefinition>}
*/
var KNOWN_KEYS = {};
_KNOWN_KEYS.forEach(function createKeyDefinitionMap(keyDefinition) {
// Construct a map of keysym to KeyDefinition object
KNOWN_KEYS[keyDefinition.keysym] = (
new Guacamole.KeyEventInterpreter.KeyDefinition(keyDefinition));
});
/**
* All key events parsed as of the most recent handleKeyEvent() invocation.
*
* @private
* @type {!Guacamole.KeyEventInterpreter.KeyEvent[]}
*/
var parsedEvents = [];
/**
* If the provided keysym corresponds to a valid UTF-8 character, return
* a KeyDefinition for that keysym. Otherwise, return null.
*
* @private
* @param {Number} keysym
* The keysym to produce a UTF-8 KeyDefinition for, if valid.
*
* @returns {Guacamole.KeyEventInterpreter.KeyDefinition}
* A KeyDefinition for the provided keysym, if it's a valid UTF-8
* keysym, or null otherwise.
*/
function getUnicodeKeyDefinition(keysym) {
// Translate only if keysym maps to Unicode
if (keysym < 0x00 || (keysym > 0xFF && (keysym | 0xFFFF) != 0x0100FFFF))
return null;
// Convert to UTF8 string
var codepoint = keysym & 0xFFFF;
var name = String.fromCharCode(codepoint);
// Create and return the definition
return new Guacamole.KeyEventInterpreter.KeyDefinition({
keysym: keysym, name: name, value: name});
}
/**
* Return a KeyDefinition corresponding to the provided keysym.
*
* @private
* @param {Number} keysym
* The keysym to return a KeyDefinition for.
*
* @returns {KeyDefinition}
* A KeyDefinition corresponding to the provided keysym.
*/
function getKeyDefinitionByKeysym(keysym) {
// If it's a known type, return the existing definition
if (keysym in KNOWN_KEYS)
return KNOWN_KEYS[keysym];
// Return a UTF-8 KeyDefinition, if valid
var definition = getUnicodeKeyDefinition(keysym);
if (definition != null)
return definition;
// If it's not UTF-8, return an unknown definition, with the name
// just set to the hex value of the keysym
return new Guacamole.KeyEventInterpreter.KeyDefinition({
keysym: keysym,
name: '0x' + String(keysym.toString(16))
})
}
/**
* Handles a raw key event, appending a new key event object for every
* handled raw event.
*
* @param {!string[]} args
* The arguments of the key event.
*/
this.handleKeyEvent = function handleKeyEvent(args) {
// The X11 keysym
var keysym = parseInt(args[0]);
// Either 1 or 0 for pressed or released, respectively
var pressed = parseInt(args[1]);
// The timestamp when this key event occured
var timestamp = parseInt(args[2]);
// The timestamp relative to the provided initial timestamp
var relativeTimestap = timestamp - startTimestamp;
// Known information about the parsed key
var definition = getKeyDefinitionByKeysym(keysym);
// Push the latest parsed event into the list
parsedEvents.push(new Guacamole.KeyEventInterpreter.KeyEvent({
definition: definition,
pressed: pressed,
timestamp: relativeTimestap
}));
};
/**
* Return the current batch of typed text. Note that the batch may be
* incomplete, as more key events might be processed before the next
* batch starts.
*
* @returns {Guacamole.KeyEventInterpreter.KeyEvent[]}
* The current batch of text.
*/
this.getEvents = function getEvents() {
return parsedEvents;
};
};
/**
* A definition for a known key.
*
* @constructor
* @param {Guacamole.KeyEventInterpreter.KeyDefinition|object} [template={}]
* The object whose properties should be copied within the new
* KeyDefinition.
*/
Guacamole.KeyEventInterpreter.KeyDefinition = function KeyDefinition(template) {
// Use empty object by default
template = template || {};
/**
* The X11 keysym of the key.
* @type {!number}
*/
this.keysym = parseInt(template.keysym);
/**
* A human-readable name for the key.
* @type {!String}
*/
this.name = template.name;
/**
* The value which would be typed in a typical text editor, if any. If the
* key is not associated with any typeable value, this will be undefined.
* @type {String}
*/
this.value = template.value;
};
/**
* A granular description of an extracted key event, including a human-readable
* text representation of the event, whether the event is directly typed or not,
* and the timestamp when the event occured.
*
* @constructor
* @param {Guacamole.KeyEventInterpreter.KeyEvent|object} [template={}]
* The object whose properties should be copied within the new
* KeyEvent.
*/
Guacamole.KeyEventInterpreter.KeyEvent = function KeyEvent(template) {
// Use empty object by default
template = template || {};
/**
* The key definition for the pressed key.
*
* @type {!Guacamole.KeyEventInterpreter.KeyDefinition}
*/
this.definition = template.definition;
/**
* True if the key was pressed to create this event, or false if it was
* released.
*
* @type {!boolean}
*/
this.pressed = !!template.pressed;
/**
* The timestamp from the recording when this event occured.
*
* @type {!Number}
*/
this.timestamp = template.timestamp;
};