summaryrefslogtreecommitdiffstats
path: root/webclients/novnc/core/input/util.js
blob: 96a5a233a839885c343634f05b6ca0f206735527 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
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)) {
        var 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';
        }

        // 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';
        }

        // IE and Edge have broken handling of AltGraph so we cannot
        // trust them for printable characters
        if ((evt.key.length !== 1) || (!browser.isIE() && !browser.isEdge())) {
            return evt.key;
        }
    }

    // Try to deduce it based on the physical key
    var 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){
    var key = getKey(evt);

    if (key === 'Unidentified') {
        return null;
    }

    // First look up special keys
    if (key in DOMKeyTable) {
        var location = evt.location;

        // Safari screws up location for the right cmd key
        if ((key === 'Meta') && (location === 0)) {
            location = 2;
        }

        if ((location === undefined) || (location > 3)) {
            location = 0;
        }

        return DOMKeyTable[key][location];
    }

    // Now we need to look at the Unicode symbol instead

    var codepoint;

    // Special key? (FIXME: Should have been caught earlier)
    if (key.length !== 1) {
        return null;
    }

    codepoint = key.charCodeAt();
    if (codepoint) {
        return keysyms.lookup(codepoint);
    }

    return null;
}