192 lines
5.9 KiB
JavaScript
192 lines
5.9 KiB
JavaScript
|
import KeyTable from "./keysym.js";
|
||
|
import keysyms from "./keysymdef.js";
|
||
|
import vkeys from "./vkeys.js";
|
||
|
import fixedkeys from "./fixedkeys.js";
|
||
|
import DOMKeyTable from "./domkeytable.js";
|
||
|
import * as browser from "../util/browser.js";
|
||
|
|
||
|
// Get 'KeyboardEvent.code', handling legacy browsers
|
||
|
export function getKeycode(evt) {
|
||
|
// Are we getting proper key identifiers?
|
||
|
// (unfortunately Firefox and Chrome are crappy here and gives
|
||
|
// us an empty string on some platforms, rather than leaving it
|
||
|
// undefined)
|
||
|
if (evt.code) {
|
||
|
// Mozilla isn't fully in sync with the spec yet
|
||
|
switch (evt.code) {
|
||
|
case 'OSLeft': return 'MetaLeft';
|
||
|
case 'OSRight': return 'MetaRight';
|
||
|
}
|
||
|
|
||
|
return evt.code;
|
||
|
}
|
||
|
|
||
|
// The de-facto standard is to use Windows Virtual-Key codes
|
||
|
// in the 'keyCode' field for non-printable characters
|
||
|
if (evt.keyCode in vkeys) {
|
||
|
let code = vkeys[evt.keyCode];
|
||
|
|
||
|
// macOS has messed up this code for some reason
|
||
|
if (browser.isMac() && (code === 'ContextMenu')) {
|
||
|
code = 'MetaRight';
|
||
|
}
|
||
|
|
||
|
// The keyCode doesn't distinguish between left and right
|
||
|
// for the standard modifiers
|
||
|
if (evt.location === 2) {
|
||
|
switch (code) {
|
||
|
case 'ShiftLeft': return 'ShiftRight';
|
||
|
case 'ControlLeft': return 'ControlRight';
|
||
|
case 'AltLeft': return 'AltRight';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Nor a bunch of the numpad keys
|
||
|
if (evt.location === 3) {
|
||
|
switch (code) {
|
||
|
case 'Delete': return 'NumpadDecimal';
|
||
|
case 'Insert': return 'Numpad0';
|
||
|
case 'End': return 'Numpad1';
|
||
|
case 'ArrowDown': return 'Numpad2';
|
||
|
case 'PageDown': return 'Numpad3';
|
||
|
case 'ArrowLeft': return 'Numpad4';
|
||
|
case 'ArrowRight': return 'Numpad6';
|
||
|
case 'Home': return 'Numpad7';
|
||
|
case 'ArrowUp': return 'Numpad8';
|
||
|
case 'PageUp': return 'Numpad9';
|
||
|
case 'Enter': return 'NumpadEnter';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return code;
|
||
|
}
|
||
|
|
||
|
return 'Unidentified';
|
||
|
}
|
||
|
|
||
|
// Get 'KeyboardEvent.key', handling legacy browsers
|
||
|
export function getKey(evt) {
|
||
|
// Are we getting a proper key value?
|
||
|
if (evt.key !== undefined) {
|
||
|
// Mozilla isn't fully in sync with the spec yet
|
||
|
switch (evt.key) {
|
||
|
case 'OS': return 'Meta';
|
||
|
case 'LaunchMyComputer': return 'LaunchApplication1';
|
||
|
case 'LaunchCalculator': return 'LaunchApplication2';
|
||
|
}
|
||
|
|
||
|
// iOS leaks some OS names
|
||
|
switch (evt.key) {
|
||
|
case 'UIKeyInputUpArrow': return 'ArrowUp';
|
||
|
case 'UIKeyInputDownArrow': return 'ArrowDown';
|
||
|
case 'UIKeyInputLeftArrow': return 'ArrowLeft';
|
||
|
case 'UIKeyInputRightArrow': return 'ArrowRight';
|
||
|
case 'UIKeyInputEscape': return 'Escape';
|
||
|
}
|
||
|
|
||
|
// Broken behaviour in Chrome
|
||
|
if ((evt.key === '\x00') && (evt.code === 'NumpadDecimal')) {
|
||
|
return 'Delete';
|
||
|
}
|
||
|
|
||
|
return evt.key;
|
||
|
}
|
||
|
|
||
|
// Try to deduce it based on the physical key
|
||
|
const code = getKeycode(evt);
|
||
|
if (code in fixedkeys) {
|
||
|
return fixedkeys[code];
|
||
|
}
|
||
|
|
||
|
// If that failed, then see if we have a printable character
|
||
|
if (evt.charCode) {
|
||
|
return String.fromCharCode(evt.charCode);
|
||
|
}
|
||
|
|
||
|
// At this point we have nothing left to go on
|
||
|
return 'Unidentified';
|
||
|
}
|
||
|
|
||
|
// Get the most reliable keysym value we can get from a key event
|
||
|
export function getKeysym(evt) {
|
||
|
const key = getKey(evt);
|
||
|
|
||
|
if (key === 'Unidentified') {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
// First look up special keys
|
||
|
if (key in DOMKeyTable) {
|
||
|
let location = evt.location;
|
||
|
|
||
|
// Safari screws up location for the right cmd key
|
||
|
if ((key === 'Meta') && (location === 0)) {
|
||
|
location = 2;
|
||
|
}
|
||
|
|
||
|
// And for Clear
|
||
|
if ((key === 'Clear') && (location === 3)) {
|
||
|
let code = getKeycode(evt);
|
||
|
if (code === 'NumLock') {
|
||
|
location = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((location === undefined) || (location > 3)) {
|
||
|
location = 0;
|
||
|
}
|
||
|
|
||
|
// The original Meta key now gets confused with the Windows key
|
||
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=1020141
|
||
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1232918
|
||
|
if (key === 'Meta') {
|
||
|
let code = getKeycode(evt);
|
||
|
if (code === 'AltLeft') {
|
||
|
return KeyTable.XK_Meta_L;
|
||
|
} else if (code === 'AltRight') {
|
||
|
return KeyTable.XK_Meta_R;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// macOS has Clear instead of NumLock, but the remote system is
|
||
|
// probably not macOS, so lying here is probably best...
|
||
|
if (key === 'Clear') {
|
||
|
let code = getKeycode(evt);
|
||
|
if (code === 'NumLock') {
|
||
|
return KeyTable.XK_Num_Lock;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Windows sends alternating symbols for some keys when using a
|
||
|
// Japanese layout. We have no way of synchronising with the IM
|
||
|
// running on the remote system, so we send some combined keysym
|
||
|
// instead and hope for the best.
|
||
|
if (browser.isWindows()) {
|
||
|
switch (key) {
|
||
|
case 'Zenkaku':
|
||
|
case 'Hankaku':
|
||
|
return KeyTable.XK_Zenkaku_Hankaku;
|
||
|
case 'Romaji':
|
||
|
case 'KanaMode':
|
||
|
return KeyTable.XK_Romaji;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return DOMKeyTable[key][location];
|
||
|
}
|
||
|
|
||
|
// Now we need to look at the Unicode symbol instead
|
||
|
|
||
|
// Special key? (FIXME: Should have been caught earlier)
|
||
|
if (key.length !== 1) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
const codepoint = key.charCodeAt();
|
||
|
if (codepoint) {
|
||
|
return keysyms.lookup(codepoint);
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|