main
Raw Download raw file
  1import KeyTable from "./keysym.js";
  2import keysyms from "./keysymdef.js";
  3import vkeys from "./vkeys.js";
  4import fixedkeys from "./fixedkeys.js";
  5import DOMKeyTable from "./domkeytable.js";
  6import * as browser from "../util/browser.js";
  7
  8// Get 'KeyboardEvent.code', handling legacy browsers
  9export function getKeycode(evt) {
 10    // Are we getting proper key identifiers?
 11    // (unfortunately Firefox and Chrome are crappy here and gives
 12    // us an empty string on some platforms, rather than leaving it
 13    // undefined)
 14    if (evt.code) {
 15        // Mozilla isn't fully in sync with the spec yet
 16        switch (evt.code) {
 17            case 'OSLeft': return 'MetaLeft';
 18            case 'OSRight': return 'MetaRight';
 19        }
 20
 21        return evt.code;
 22    }
 23
 24    // The de-facto standard is to use Windows Virtual-Key codes
 25    // in the 'keyCode' field for non-printable characters
 26    if (evt.keyCode in vkeys) {
 27        let code = vkeys[evt.keyCode];
 28
 29        // macOS has messed up this code for some reason
 30        if (browser.isMac() && (code === 'ContextMenu')) {
 31            code = 'MetaRight';
 32        }
 33
 34        // The keyCode doesn't distinguish between left and right
 35        // for the standard modifiers
 36        if (evt.location === 2) {
 37            switch (code) {
 38                case 'ShiftLeft': return 'ShiftRight';
 39                case 'ControlLeft': return 'ControlRight';
 40                case 'AltLeft': return 'AltRight';
 41            }
 42        }
 43
 44        // Nor a bunch of the numpad keys
 45        if (evt.location === 3) {
 46            switch (code) {
 47                case 'Delete': return 'NumpadDecimal';
 48                case 'Insert': return 'Numpad0';
 49                case 'End': return 'Numpad1';
 50                case 'ArrowDown': return 'Numpad2';
 51                case 'PageDown': return 'Numpad3';
 52                case 'ArrowLeft': return 'Numpad4';
 53                case 'ArrowRight': return 'Numpad6';
 54                case 'Home': return 'Numpad7';
 55                case 'ArrowUp': return 'Numpad8';
 56                case 'PageUp': return 'Numpad9';
 57                case 'Enter': return 'NumpadEnter';
 58            }
 59        }
 60
 61        return code;
 62    }
 63
 64    return 'Unidentified';
 65}
 66
 67// Get 'KeyboardEvent.key', handling legacy browsers
 68export function getKey(evt) {
 69    // Are we getting a proper key value?
 70    if ((evt.key !== undefined) && (evt.key !== 'Unidentified')) {
 71        // Mozilla isn't fully in sync with the spec yet
 72        switch (evt.key) {
 73            case 'OS': return 'Meta';
 74            case 'LaunchMyComputer': return 'LaunchApplication1';
 75            case 'LaunchCalculator': return 'LaunchApplication2';
 76        }
 77
 78        // iOS leaks some OS names
 79        switch (evt.key) {
 80            case 'UIKeyInputUpArrow': return 'ArrowUp';
 81            case 'UIKeyInputDownArrow': return 'ArrowDown';
 82            case 'UIKeyInputLeftArrow': return 'ArrowLeft';
 83            case 'UIKeyInputRightArrow': return 'ArrowRight';
 84            case 'UIKeyInputEscape': return 'Escape';
 85        }
 86
 87        // Broken behaviour in Chrome
 88        if ((evt.key === '\x00') && (evt.code === 'NumpadDecimal')) {
 89            return 'Delete';
 90        }
 91
 92        return evt.key;
 93    }
 94
 95    // Try to deduce it based on the physical key
 96    const code = getKeycode(evt);
 97    if (code in fixedkeys) {
 98        return fixedkeys[code];
 99    }
100
101    // If that failed, then see if we have a printable character
102    if (evt.charCode) {
103        return String.fromCharCode(evt.charCode);
104    }
105
106    // At this point we have nothing left to go on
107    return 'Unidentified';
108}
109
110// Get the most reliable keysym value we can get from a key event
111export function getKeysym(evt) {
112    const key = getKey(evt);
113
114    if (key === 'Unidentified') {
115        return null;
116    }
117
118    // First look up special keys
119    if (key in DOMKeyTable) {
120        let location = evt.location;
121
122        // Safari screws up location for the right cmd key
123        if ((key === 'Meta') && (location === 0)) {
124            location = 2;
125        }
126
127        // And for Clear
128        if ((key === 'Clear') && (location === 3)) {
129            let code = getKeycode(evt);
130            if (code === 'NumLock') {
131                location = 0;
132            }
133        }
134
135        if ((location === undefined) || (location > 3)) {
136            location = 0;
137        }
138
139        // The original Meta key now gets confused with the Windows key
140        // https://bugs.chromium.org/p/chromium/issues/detail?id=1020141
141        // https://bugzilla.mozilla.org/show_bug.cgi?id=1232918
142        if (key === 'Meta') {
143            let code = getKeycode(evt);
144            if (code === 'AltLeft') {
145                return KeyTable.XK_Meta_L;
146            } else if (code === 'AltRight') {
147                return KeyTable.XK_Meta_R;
148            }
149        }
150
151        // macOS has Clear instead of NumLock, but the remote system is
152        // probably not macOS, so lying here is probably best...
153        if (key === 'Clear') {
154            let code = getKeycode(evt);
155            if (code === 'NumLock') {
156                return KeyTable.XK_Num_Lock;
157            }
158        }
159
160        // Windows sends alternating symbols for some keys when using a
161        // Japanese layout. We have no way of synchronising with the IM
162        // running on the remote system, so we send some combined keysym
163        // instead and hope for the best.
164        if (browser.isWindows()) {
165            switch (key) {
166                case 'Zenkaku':
167                case 'Hankaku':
168                    return KeyTable.XK_Zenkaku_Hankaku;
169                case 'Romaji':
170                case 'KanaMode':
171                    return KeyTable.XK_Romaji;
172            }
173        }
174
175        return DOMKeyTable[key][location];
176    }
177
178    // Now we need to look at the Unicode symbol instead
179
180    // Special key? (FIXME: Should have been caught earlier)
181    if (key.length !== 1) {
182        return null;
183    }
184
185    const codepoint = key.charCodeAt();
186    if (codepoint) {
187        return keysyms.lookup(codepoint);
188    }
189
190    return null;
191}