224 lines
6.8 KiB
JavaScript
224 lines
6.8 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) && (evt.key !== 'Unidentified')) {
|
||
// 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];
|
||
}
|
||
|
||
if(key === "Dead" ){
|
||
return getDeadKeysym(evt);
|
||
}
|
||
|
||
// 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;
|
||
}
|
||
|
||
// Try to guess Keysym for Dead key. For now only should work for US-int
|
||
// TODO test
|
||
// TODO try to find and implement Dead keys for more keyboard layouts
|
||
export function getDeadKeysym(evt) {
|
||
switch(evt.code){
|
||
case "Quote":
|
||
if (evt.shiftKey){
|
||
return KeyTable.XK_dead_diaeresis //
|
||
} else {
|
||
return KeyTable.XK_dead_acute // ´
|
||
}
|
||
case "Backquote":
|
||
if (evt.shiftKey){
|
||
return KeyTable.XK_dead_grave
|
||
} else {
|
||
return KeyTable.XK_dead_tilde
|
||
}
|
||
case 'Digit6':
|
||
if (evt.shiftKey){
|
||
return KeyTable.XK_dead_circumflex
|
||
}
|
||
|
||
default:
|
||
console.log(evt)
|
||
return null
|
||
}
|
||
}
|