]>
git.proxmox.com Git - mirror_novnc.git/blob - core/input/keyboard.js
2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2012 Joel Martin
4 * Copyright (C) 2013 Samuel Mannehed for Cendio AB
5 * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
8 /*jslint browser: true, white: false */
9 /*global window, Util */
11 import * as Log
from '../util/logging.js';
12 import { stopEvent
} from '../util/events.js';
13 import { set_defaults
, make_properties
} from '../util/properties.js';
14 import * as KeyboardUtil
from "./util.js";
15 import KeyTable
from "./keysym.js";
18 // Keyboard event handler
21 export default function Keyboard(defaults
) {
22 this._keyDownList
= {}; // List of depressed keys
23 // (even if they are happy)
24 this._pendingKey
= null; // Key waiting for keypress
26 set_defaults(this, defaults
, {
31 // keep these here so we can refer to them later
32 this._eventHandlers
= {
33 'keyup': this._handleKeyUp
.bind(this),
34 'keydown': this._handleKeyDown
.bind(this),
35 'keypress': this._handleKeyPress
.bind(this),
36 'blur': this._allKeysUp
.bind(this)
41 return navigator
&& !!(/mac/i).exec(navigator
.platform
);
43 function isWindows() {
44 return navigator
&& !!(/win/i).exec(navigator
.platform
);
48 (!!(/ipad/i).exec(navigator
.platform
) ||
49 !!(/iphone/i).exec(navigator
.platform
) ||
50 !!(/ipod/i).exec(navigator
.platform
));
53 return navigator
&& !!(/trident/i).exec(navigator
.userAgent
);
56 return navigator
&& !!(/edge/i).exec(navigator
.userAgent
);
59 Keyboard
.prototype = {
62 _sendKeyEvent: function (keysym
, code
, down
) {
63 if (!this._onKeyEvent
) {
67 Log
.Debug("onKeyEvent " + (down
? "down" : "up") +
68 ", keysym: " + keysym
, ", code: " + code
);
70 // Windows sends CtrlLeft+AltRight when you press
71 // AltGraph, which tends to confuse the hell out of
72 // remote systems. Fake a release of these keys until
73 // there is a way to detect AltGraph properly.
74 var fakeAltGraph
= false;
75 if (down
&& isWindows()) {
76 if ((code
!== 'ControlLeft') &&
77 (code
!== 'AltRight') &&
78 ('ControlLeft' in this._keyDownList
) &&
79 ('AltRight' in this._keyDownList
)) {
81 this._onKeyEvent(this._keyDownList
['AltRight'],
83 this._onKeyEvent(this._keyDownList
['ControlLeft'],
84 'ControlLeft', false);
88 this._onKeyEvent(keysym
, code
, down
);
91 this._onKeyEvent(this._keyDownList
['ControlLeft'],
93 this._onKeyEvent(this._keyDownList
['AltRight'],
98 _getKeyCode: function (e
) {
99 var code
= KeyboardUtil
.getKeycode(e
);
100 if (code
!== 'Unidentified') {
104 // Unstable, but we don't have anything else to go on
105 // (don't use it for 'keypress' events thought since
106 // WebKit sets it to the same as charCode)
107 if (e
.keyCode
&& (e
.type
!== 'keypress')) {
108 // 229 is used for composition events
109 if (e
.keyCode
!== 229) {
110 return 'Platform' + e
.keyCode
;
114 // A precursor to the final DOM3 standard. Unfortunately it
115 // is not layout independent, so it is as bad as using keyCode
116 if (e
.keyIdentifier
) {
117 // Non-character key?
118 if (e
.keyIdentifier
.substr(0, 2) !== 'U+') {
119 return e
.keyIdentifier
;
122 var codepoint
= parseInt(e
.keyIdentifier
.substr(2), 16);
123 var char = String
.fromCharCode(codepoint
);
124 // Some implementations fail to uppercase the symbols
125 char = char.toUpperCase();
127 return 'Platform' + char.charCodeAt();
130 return 'Unidentified';
133 _handleKeyDown: function (e
) {
134 if (!this._focused
) { return; }
136 var code
= this._getKeyCode(e
);
137 var keysym
= KeyboardUtil
.getKeysym(e
);
139 // We cannot handle keys we cannot track, but we also need
140 // to deal with virtual keyboards which omit key info
141 // (iOS omits tracking info on keyup events, which forces us to
142 // special treat that platform here)
143 if ((code
=== 'Unidentified') || isIOS()) {
145 // If it's a virtual keyboard then it should be
146 // sufficient to just send press and release right
148 this._sendKeyEvent(keysym
, code
, true);
149 this._sendKeyEvent(keysym
, code
, false);
156 // Alt behaves more like AltGraph on macOS, so shuffle the
157 // keys around a bit to make things more sane for the remote
158 // server. This method is used by RealVNC and TigerVNC (and
162 case KeyTable
.XK_Super_L
:
163 keysym
= KeyTable
.XK_Alt_L
;
165 case KeyTable
.XK_Super_R
:
166 keysym
= KeyTable
.XK_Super_L
;
168 case KeyTable
.XK_Alt_L
:
169 keysym
= KeyTable
.XK_Mode_switch
;
171 case KeyTable
.XK_Alt_R
:
172 keysym
= KeyTable
.XK_ISO_Level3_Shift
;
177 // Is this key already pressed? If so, then we must use the
178 // same keysym or we'll confuse the server
179 if (code
in this._keyDownList
) {
180 keysym
= this._keyDownList
[code
];
183 // macOS doesn't send proper key events for modifiers, only
184 // state change events. That gets extra confusing for CapsLock
185 // which toggles on each press, but not on release. So pretend
186 // it was a quick press and release of the button.
187 if (isMac() && (code
=== 'CapsLock')) {
188 this._sendKeyEvent(KeyTable
.XK_Caps_Lock
, 'CapsLock', true);
189 this._sendKeyEvent(KeyTable
.XK_Caps_Lock
, 'CapsLock', false);
194 // If this is a legacy browser then we'll need to wait for
195 // a keypress event as well
196 // (IE and Edge has a broken KeyboardEvent.key, so we can't
197 // just check for the presence of that field)
198 if (!keysym
&& (!e
.key
|| isIE() || isEdge())) {
199 this._pendingKey
= code
;
200 // However we might not get a keypress event if the key
201 // is non-printable, which needs some special fallback
203 setTimeout(this._handleKeyPressTimeout
.bind(this), 10, e
);
207 this._pendingKey
= null;
210 this._keyDownList
[code
] = keysym
;
212 this._sendKeyEvent(keysym
, code
, true);
215 // Legacy event for browsers without code/key
216 _handleKeyPress: function (e
) {
217 if (!this._focused
) { return; }
221 // Are we expecting a keypress?
222 if (this._pendingKey
=== null) {
226 var code
= this._getKeyCode(e
);
227 var keysym
= KeyboardUtil
.getKeysym(e
);
229 // The key we were waiting for?
230 if ((code
!== 'Unidentified') && (code
!= this._pendingKey
)) {
234 code
= this._pendingKey
;
235 this._pendingKey
= null;
238 console
.log('keypress with no keysym:', e
);
242 this._keyDownList
[code
] = keysym
;
244 this._sendKeyEvent(keysym
, code
, true);
246 _handleKeyPressTimeout: function (e
) {
247 if (!this._focused
) { return; }
249 // Did someone manage to sort out the key already?
250 if (this._pendingKey
=== null) {
256 code
= this._pendingKey
;
257 this._pendingKey
= null;
259 // We have no way of knowing the proper keysym with the
260 // information given, but the following are true for most
262 if ((e
.keyCode
>= 0x30) && (e
.keyCode
<= 0x39)) {
265 } else if ((e
.keyCode
>= 0x41) && (e
.keyCode
<= 0x5a)) {
267 var char = String
.fromCharCode(e
.keyCode
);
268 // A feeble attempt at the correct case
270 char = char.toUpperCase();
272 char = char.toLowerCase();
273 keysym
= char.charCodeAt();
279 this._keyDownList
[code
] = keysym
;
281 this._sendKeyEvent(keysym
, code
, true);
284 _handleKeyUp: function (e
) {
285 if (!this._focused
) { return; }
289 var code
= this._getKeyCode(e
);
291 // See comment in _handleKeyDown()
292 if (isMac() && (code
=== 'CapsLock')) {
293 this._sendKeyEvent(KeyTable
.XK_Caps_Lock
, 'CapsLock', true);
294 this._sendKeyEvent(KeyTable
.XK_Caps_Lock
, 'CapsLock', false);
298 // Do we really think this key is down?
299 if (!(code
in this._keyDownList
)) {
303 this._sendKeyEvent(this._keyDownList
[code
], code
, false);
305 delete this._keyDownList
[code
];
308 _allKeysUp: function () {
309 Log
.Debug(">> Keyboard.allKeysUp");
310 for (var code
in this._keyDownList
) {
311 this._sendKeyEvent(this._keyDownList
[code
], code
, false);
313 this._keyDownList
= {};
314 Log
.Debug("<< Keyboard.allKeysUp");
320 //Log.Debug(">> Keyboard.grab");
321 var c
= this._target
;
323 c
.addEventListener('keydown', this._eventHandlers
.keydown
);
324 c
.addEventListener('keyup', this._eventHandlers
.keyup
);
325 c
.addEventListener('keypress', this._eventHandlers
.keypress
);
327 // Release (key up) if window loses focus
328 window
.addEventListener('blur', this._eventHandlers
.blur
);
330 //Log.Debug("<< Keyboard.grab");
333 ungrab: function () {
334 //Log.Debug(">> Keyboard.ungrab");
335 var c
= this._target
;
337 c
.removeEventListener('keydown', this._eventHandlers
.keydown
);
338 c
.removeEventListener('keyup', this._eventHandlers
.keyup
);
339 c
.removeEventListener('keypress', this._eventHandlers
.keypress
);
340 window
.removeEventListener('blur', this._eventHandlers
.blur
);
342 // Release (key up) all keys that are in a down state
345 //Log.Debug(">> Keyboard.ungrab");
349 make_properties(Keyboard
, [
350 ['target', 'wo', 'dom'], // DOM element that captures keyboard input
351 ['focused', 'rw', 'bool'], // Capture and send key events
353 ['onKeyEvent', 'rw', 'func'] // Handler for key press/release