]> git.proxmox.com Git - mirror_novnc.git/blame - core/input/keyboard.js
Remove console statements
[mirror_novnc.git] / core / input / keyboard.js
CommitLineData
d3796c14
JM
1/*
2 * noVNC: HTML5 VNC client
1d728ace 3 * Copyright (C) 2012 Joel Martin
b2f1961a 4 * Copyright (C) 2013 Samuel Mannehed for Cendio AB
1d728ace 5 * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
d3796c14
JM
6 */
7
d6e281ba 8/*jslint browser: true, white: false */
d3796c14
JM
9/*global window, Util */
10
6d6f0db0 11import * as Log from '../util/logging.js';
c1e2785f 12import { stopEvent } from '../util/events.js';
6d6f0db0 13import * as KeyboardUtil from "./util.js";
bf43c263 14import KeyTable from "./keysym.js";
6d6f0db0
SR
15
16//
17// Keyboard event handler
18//
19
747b4623 20export default function Keyboard(target) {
3d7bb020
PO
21 this._target = target || null;
22
ae820533 23 this._keyDownList = {}; // List of depressed keys
6d6f0db0 24 // (even if they are happy)
9fce233d 25 this._pendingKey = null; // Key waiting for keypress
6d6f0db0 26
6d6f0db0
SR
27 // keep these here so we can refer to them later
28 this._eventHandlers = {
29 'keyup': this._handleKeyUp.bind(this),
30 'keydown': this._handleKeyDown.bind(this),
31 'keypress': this._handleKeyPress.bind(this),
32 'blur': this._allKeysUp.bind(this)
33 };
34};
d6e281ba 35
bf43c263
PO
36function isMac() {
37 return navigator && !!(/mac/i).exec(navigator.platform);
38}
39function isWindows() {
40 return navigator && !!(/win/i).exec(navigator.platform);
41}
9e99ce12
PO
42function isIOS() {
43 return navigator &&
44 (!!(/ipad/i).exec(navigator.platform) ||
45 !!(/iphone/i).exec(navigator.platform) ||
46 !!(/ipod/i).exec(navigator.platform));
47}
844e9839
PO
48function isIE() {
49 return navigator && !!(/trident/i).exec(navigator.userAgent);
50}
51function isEdge() {
52 return navigator && !!(/edge/i).exec(navigator.userAgent);
53}
bf43c263 54
6d6f0db0 55Keyboard.prototype = {
747b4623 56 // ===== EVENT HANDLERS =====
d6e281ba 57
747b4623 58 onkeyevent: function () {}, // Handler for key press/release
f7363fd2 59
747b4623
PO
60 // ===== PRIVATE METHODS =====
61
62 _sendKeyEvent: function (keysym, code, down) {
63 Log.Debug("onkeyevent " + (down ? "down" : "up") +
f7363fd2
PO
64 ", keysym: " + keysym, ", code: " + code);
65
bf43c263
PO
66 // Windows sends CtrlLeft+AltRight when you press
67 // AltGraph, which tends to confuse the hell out of
68 // remote systems. Fake a release of these keys until
69 // there is a way to detect AltGraph properly.
70 var fakeAltGraph = false;
71 if (down && isWindows()) {
72 if ((code !== 'ControlLeft') &&
73 (code !== 'AltRight') &&
74 ('ControlLeft' in this._keyDownList) &&
75 ('AltRight' in this._keyDownList)) {
76 fakeAltGraph = true;
747b4623 77 this.onkeyevent(this._keyDownList['AltRight'],
bf43c263 78 'AltRight', false);
747b4623 79 this.onkeyevent(this._keyDownList['ControlLeft'],
bf43c263
PO
80 'ControlLeft', false);
81 }
82 }
83
747b4623 84 this.onkeyevent(keysym, code, down);
bf43c263
PO
85
86 if (fakeAltGraph) {
747b4623 87 this.onkeyevent(this._keyDownList['ControlLeft'],
bf43c263 88 'ControlLeft', true);
747b4623 89 this.onkeyevent(this._keyDownList['AltRight'],
bf43c263
PO
90 'AltRight', true);
91 }
f7363fd2
PO
92 },
93
94 _getKeyCode: function (e) {
95 var code = KeyboardUtil.getKeycode(e);
7e79dfe4
PO
96 if (code !== 'Unidentified') {
97 return code;
98 }
99
100 // Unstable, but we don't have anything else to go on
101 // (don't use it for 'keypress' events thought since
102 // WebKit sets it to the same as charCode)
103 if (e.keyCode && (e.type !== 'keypress')) {
4093c37f
PO
104 // 229 is used for composition events
105 if (e.keyCode !== 229) {
106 return 'Platform' + e.keyCode;
107 }
7e79dfe4
PO
108 }
109
110 // A precursor to the final DOM3 standard. Unfortunately it
111 // is not layout independent, so it is as bad as using keyCode
112 if (e.keyIdentifier) {
113 // Non-character key?
114 if (e.keyIdentifier.substr(0, 2) !== 'U+') {
115 return e.keyIdentifier;
f7363fd2 116 }
7e79dfe4
PO
117
118 var codepoint = parseInt(e.keyIdentifier.substr(2), 16);
119 var char = String.fromCharCode(codepoint);
120 // Some implementations fail to uppercase the symbols
121 char = char.toUpperCase();
122
123 return 'Platform' + char.charCodeAt();
f7363fd2
PO
124 }
125
7e79dfe4 126 return 'Unidentified';
6d6f0db0 127 },
d6e281ba 128
6d6f0db0 129 _handleKeyDown: function (e) {
f7363fd2
PO
130 var code = this._getKeyCode(e);
131 var keysym = KeyboardUtil.getKeysym(e);
132
ae820533
PO
133 // We cannot handle keys we cannot track, but we also need
134 // to deal with virtual keyboards which omit key info
9e99ce12
PO
135 // (iOS omits tracking info on keyup events, which forces us to
136 // special treat that platform here)
137 if ((code === 'Unidentified') || isIOS()) {
ae820533
PO
138 if (keysym) {
139 // If it's a virtual keyboard then it should be
140 // sufficient to just send press and release right
141 // after each other
9e99ce12
PO
142 this._sendKeyEvent(keysym, code, true);
143 this._sendKeyEvent(keysym, code, false);
ae820533
PO
144 }
145
146 stopEvent(e);
147 return;
148 }
149
bf43c263
PO
150 // Alt behaves more like AltGraph on macOS, so shuffle the
151 // keys around a bit to make things more sane for the remote
152 // server. This method is used by RealVNC and TigerVNC (and
153 // possibly others).
154 if (isMac()) {
155 switch (keysym) {
156 case KeyTable.XK_Super_L:
157 keysym = KeyTable.XK_Alt_L;
158 break;
159 case KeyTable.XK_Super_R:
160 keysym = KeyTable.XK_Super_L;
161 break;
162 case KeyTable.XK_Alt_L:
163 keysym = KeyTable.XK_Mode_switch;
164 break;
165 case KeyTable.XK_Alt_R:
166 keysym = KeyTable.XK_ISO_Level3_Shift;
167 break;
168 }
169 }
170
ae820533
PO
171 // Is this key already pressed? If so, then we must use the
172 // same keysym or we'll confuse the server
173 if (code in this._keyDownList) {
174 keysym = this._keyDownList[code];
175 }
176
634cc1ba
PO
177 // macOS doesn't send proper key events for modifiers, only
178 // state change events. That gets extra confusing for CapsLock
179 // which toggles on each press, but not on release. So pretend
180 // it was a quick press and release of the button.
181 if (isMac() && (code === 'CapsLock')) {
182 this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
183 this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
184 stopEvent(e);
185 return;
186 }
187
f7363fd2 188 // If this is a legacy browser then we'll need to wait for
9fce233d 189 // a keypress event as well
844e9839
PO
190 // (IE and Edge has a broken KeyboardEvent.key, so we can't
191 // just check for the presence of that field)
192 if (!keysym && (!e.key || isIE() || isEdge())) {
9fce233d 193 this._pendingKey = code;
7cac5c8e
PO
194 // However we might not get a keypress event if the key
195 // is non-printable, which needs some special fallback
196 // handling
197 setTimeout(this._handleKeyPressTimeout.bind(this), 10, e);
9fce233d 198 return;
f7363fd2
PO
199 }
200
9fce233d
PO
201 this._pendingKey = null;
202 stopEvent(e);
203
ae820533 204 this._keyDownList[code] = keysym;
f7363fd2 205
f7363fd2 206 this._sendKeyEvent(keysym, code, true);
6d6f0db0 207 },
d6e281ba 208
f7363fd2 209 // Legacy event for browsers without code/key
6d6f0db0 210 _handleKeyPress: function (e) {
f7363fd2
PO
211 stopEvent(e);
212
9fce233d
PO
213 // Are we expecting a keypress?
214 if (this._pendingKey === null) {
215 return;
216 }
217
f7363fd2
PO
218 var code = this._getKeyCode(e);
219 var keysym = KeyboardUtil.getKeysym(e);
220
9fce233d
PO
221 // The key we were waiting for?
222 if ((code !== 'Unidentified') && (code != this._pendingKey)) {
223 return;
224 }
225
226 code = this._pendingKey;
227 this._pendingKey = null;
228
f7363fd2 229 if (!keysym) {
a0035359 230 Log.Info('keypress with no keysym:', e);
f7363fd2
PO
231 return;
232 }
233
ae820533 234 this._keyDownList[code] = keysym;
f7363fd2 235
f7363fd2 236 this._sendKeyEvent(keysym, code, true);
6d6f0db0 237 },
7cac5c8e 238 _handleKeyPressTimeout: function (e) {
7cac5c8e
PO
239 // Did someone manage to sort out the key already?
240 if (this._pendingKey === null) {
241 return;
242 }
243
244 var code, keysym;
245
246 code = this._pendingKey;
247 this._pendingKey = null;
248
249 // We have no way of knowing the proper keysym with the
250 // information given, but the following are true for most
251 // layouts
252 if ((e.keyCode >= 0x30) && (e.keyCode <= 0x39)) {
253 // Digit
254 keysym = e.keyCode;
255 } else if ((e.keyCode >= 0x41) && (e.keyCode <= 0x5a)) {
256 // Character (A-Z)
257 var char = String.fromCharCode(e.keyCode);
258 // A feeble attempt at the correct case
259 if (e.shiftKey)
260 char = char.toUpperCase();
261 else
262 char = char.toLowerCase();
263 keysym = char.charCodeAt();
264 } else {
265 // Unknown, give up
266 keysym = 0;
267 }
268
269 this._keyDownList[code] = keysym;
270
271 this._sendKeyEvent(keysym, code, true);
272 },
d3796c14 273
6d6f0db0 274 _handleKeyUp: function (e) {
f7363fd2
PO
275 stopEvent(e);
276
f7363fd2
PO
277 var code = this._getKeyCode(e);
278
634cc1ba
PO
279 // See comment in _handleKeyDown()
280 if (isMac() && (code === 'CapsLock')) {
281 this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
282 this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
283 return;
284 }
285
ae820533
PO
286 // Do we really think this key is down?
287 if (!(code in this._keyDownList)) {
f7363fd2
PO
288 return;
289 }
f7363fd2 290
ae820533
PO
291 this._sendKeyEvent(this._keyDownList[code], code, false);
292
293 delete this._keyDownList[code];
6d6f0db0 294 },
d3796c14 295
6d6f0db0
SR
296 _allKeysUp: function () {
297 Log.Debug(">> Keyboard.allKeysUp");
ae820533
PO
298 for (var code in this._keyDownList) {
299 this._sendKeyEvent(this._keyDownList[code], code, false);
f7363fd2 300 };
ae820533 301 this._keyDownList = {};
6d6f0db0
SR
302 Log.Debug("<< Keyboard.allKeysUp");
303 },
d6e281ba 304
747b4623 305 // ===== PUBLIC METHODS =====
d6e281ba 306
6d6f0db0
SR
307 grab: function () {
308 //Log.Debug(">> Keyboard.grab");
309 var c = this._target;
d6e281ba 310
6d6f0db0
SR
311 c.addEventListener('keydown', this._eventHandlers.keydown);
312 c.addEventListener('keyup', this._eventHandlers.keyup);
313 c.addEventListener('keypress', this._eventHandlers.keypress);
d6e281ba 314
6d6f0db0
SR
315 // Release (key up) if window loses focus
316 window.addEventListener('blur', this._eventHandlers.blur);
d6e281ba 317
6d6f0db0
SR
318 //Log.Debug("<< Keyboard.grab");
319 },
d6e281ba 320
6d6f0db0
SR
321 ungrab: function () {
322 //Log.Debug(">> Keyboard.ungrab");
323 var c = this._target;
d6e281ba 324
6d6f0db0
SR
325 c.removeEventListener('keydown', this._eventHandlers.keydown);
326 c.removeEventListener('keyup', this._eventHandlers.keyup);
327 c.removeEventListener('keypress', this._eventHandlers.keypress);
328 window.removeEventListener('blur', this._eventHandlers.blur);
d6e281ba 329
6d6f0db0
SR
330 // Release (key up) all keys that are in a down state
331 this._allKeysUp();
d3796c14 332
6d6f0db0
SR
333 //Log.Debug(">> Keyboard.ungrab");
334 },
6d6f0db0 335};