You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
6.7 KiB
209 lines
6.7 KiB
3 months ago
|
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. However
|
||
|
// Webkit sets it to the same as charCode in 'keypress' events.
|
||
|
if ((evt.type !== 'keypress') && (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) {
|
||
|
// IE and Edge use some ancient version of the spec
|
||
|
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8860571/
|
||
|
switch (evt.key) {
|
||
|
case 'Spacebar': return ' ';
|
||
|
case 'Esc': return 'Escape';
|
||
|
case 'Scroll': return 'ScrollLock';
|
||
|
case 'Win': return 'Meta';
|
||
|
case 'Apps': return 'ContextMenu';
|
||
|
case 'Up': return 'ArrowUp';
|
||
|
case 'Left': return 'ArrowLeft';
|
||
|
case 'Right': return 'ArrowRight';
|
||
|
case 'Down': return 'ArrowDown';
|
||
|
case 'Del': return 'Delete';
|
||
|
case 'Divide': return '/';
|
||
|
case 'Multiply': return '*';
|
||
|
case 'Subtract': return '-';
|
||
|
case 'Add': return '+';
|
||
|
case 'Decimal': return evt.char;
|
||
|
}
|
||
|
|
||
|
// 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';
|
||
|
}
|
||
|
|
||
|
// IE and Edge need special handling, but for everyone else we
|
||
|
// can trust the value provided
|
||
|
if (!browser.isIE() && !browser.isEdge()) {
|
||
|
return evt.key;
|
||
|
}
|
||
|
|
||
|
// IE and Edge have broken handling of AltGraph so we can only
|
||
|
// trust them for non-printable characters (and unfortunately
|
||
|
// they also specify 'Unidentified' for some problem keys)
|
||
|
if ((evt.key.length !== 1) && (evt.key !== 'Unidentified')) {
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|