mirror of
https://github.com/sol1/rustguac.git
synced 2026-09-10 17:47:13 +00:00
67101e27ce
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
336 lines
11 KiB
JavaScript
336 lines
11 KiB
JavaScript
/*
|
|
* 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;
|
|
|
|
};
|