]>
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 import * as Log
from '../util/logging.js';
9 import { stopEvent
} from '../util/events.js';
10 import * as KeyboardUtil
from "./util.js";
11 import KeyTable
from "./keysym.js";
14 // Keyboard event handler
17 export default function Keyboard(target
) {
18 this._target
= target
|| null;
20 this._keyDownList
= {}; // List of depressed keys
21 // (even if they are happy)
22 this._pendingKey
= null; // Key waiting for keypress
24 // keep these here so we can refer to them later
25 this._eventHandlers
= {
26 'keyup': this._handleKeyUp
.bind(this),
27 'keydown': this._handleKeyDown
.bind(this),
28 'keypress': this._handleKeyPress
.bind(this),
29 'blur': this._allKeysUp
.bind(this)
34 return navigator
&& !!(/mac/i).exec(navigator
.platform
);
36 function isWindows() {
37 return navigator
&& !!(/win/i).exec(navigator
.platform
);
41 (!!(/ipad/i).exec(navigator
.platform
) ||
42 !!(/iphone/i).exec(navigator
.platform
) ||
43 !!(/ipod/i).exec(navigator
.platform
));
46 return navigator
&& !!(/trident/i).exec(navigator
.userAgent
);
49 return navigator
&& !!(/edge/i).exec(navigator
.userAgent
);
52 Keyboard
.prototype = {
53 // ===== EVENT HANDLERS =====
55 onkeyevent: function () {}, // Handler for key press/release
57 // ===== PRIVATE METHODS =====
59 _sendKeyEvent: function (keysym
, code
, down
) {
60 Log
.Debug("onkeyevent " + (down
? "down" : "up") +
61 ", keysym: " + keysym
, ", code: " + code
);
63 // Windows sends CtrlLeft+AltRight when you press
64 // AltGraph, which tends to confuse the hell out of
65 // remote systems. Fake a release of these keys until
66 // there is a way to detect AltGraph properly.
67 var fakeAltGraph
= false;
68 if (down
&& isWindows()) {
69 if ((code
!== 'ControlLeft') &&
70 (code
!== 'AltRight') &&
71 ('ControlLeft' in this._keyDownList
) &&
72 ('AltRight' in this._keyDownList
)) {
74 this.onkeyevent(this._keyDownList
['AltRight'],
76 this.onkeyevent(this._keyDownList
['ControlLeft'],
77 'ControlLeft', false);
81 this.onkeyevent(keysym
, code
, down
);
84 this.onkeyevent(this._keyDownList
['ControlLeft'],
86 this.onkeyevent(this._keyDownList
['AltRight'],
91 _getKeyCode: function (e
) {
92 var code
= KeyboardUtil
.getKeycode(e
);
93 if (code
!== 'Unidentified') {
97 // Unstable, but we don't have anything else to go on
98 // (don't use it for 'keypress' events thought since
99 // WebKit sets it to the same as charCode)
100 if (e
.keyCode
&& (e
.type
!== 'keypress')) {
101 // 229 is used for composition events
102 if (e
.keyCode
!== 229) {
103 return 'Platform' + e
.keyCode
;
107 // A precursor to the final DOM3 standard. Unfortunately it
108 // is not layout independent, so it is as bad as using keyCode
109 if (e
.keyIdentifier
) {
110 // Non-character key?
111 if (e
.keyIdentifier
.substr(0, 2) !== 'U+') {
112 return e
.keyIdentifier
;
115 var codepoint
= parseInt(e
.keyIdentifier
.substr(2), 16);
116 var char = String
.fromCharCode(codepoint
);
117 // Some implementations fail to uppercase the symbols
118 char = char.toUpperCase();
120 return 'Platform' + char.charCodeAt();
123 return 'Unidentified';
126 _handleKeyDown: function (e
) {
127 var code
= this._getKeyCode(e
);
128 var keysym
= KeyboardUtil
.getKeysym(e
);
130 // We cannot handle keys we cannot track, but we also need
131 // to deal with virtual keyboards which omit key info
132 // (iOS omits tracking info on keyup events, which forces us to
133 // special treat that platform here)
134 if ((code
=== 'Unidentified') || isIOS()) {
136 // If it's a virtual keyboard then it should be
137 // sufficient to just send press and release right
139 this._sendKeyEvent(keysym
, code
, true);
140 this._sendKeyEvent(keysym
, code
, false);
147 // Alt behaves more like AltGraph on macOS, so shuffle the
148 // keys around a bit to make things more sane for the remote
149 // server. This method is used by RealVNC and TigerVNC (and
153 case KeyTable
.XK_Super_L
:
154 keysym
= KeyTable
.XK_Alt_L
;
156 case KeyTable
.XK_Super_R
:
157 keysym
= KeyTable
.XK_Super_L
;
159 case KeyTable
.XK_Alt_L
:
160 keysym
= KeyTable
.XK_Mode_switch
;
162 case KeyTable
.XK_Alt_R
:
163 keysym
= KeyTable
.XK_ISO_Level3_Shift
;
168 // Is this key already pressed? If so, then we must use the
169 // same keysym or we'll confuse the server
170 if (code
in this._keyDownList
) {
171 keysym
= this._keyDownList
[code
];
174 // macOS doesn't send proper key events for modifiers, only
175 // state change events. That gets extra confusing for CapsLock
176 // which toggles on each press, but not on release. So pretend
177 // it was a quick press and release of the button.
178 if (isMac() && (code
=== 'CapsLock')) {
179 this._sendKeyEvent(KeyTable
.XK_Caps_Lock
, 'CapsLock', true);
180 this._sendKeyEvent(KeyTable
.XK_Caps_Lock
, 'CapsLock', false);
185 // If this is a legacy browser then we'll need to wait for
186 // a keypress event as well
187 // (IE and Edge has a broken KeyboardEvent.key, so we can't
188 // just check for the presence of that field)
189 if (!keysym
&& (!e
.key
|| isIE() || isEdge())) {
190 this._pendingKey
= code
;
191 // However we might not get a keypress event if the key
192 // is non-printable, which needs some special fallback
194 setTimeout(this._handleKeyPressTimeout
.bind(this), 10, e
);
198 this._pendingKey
= null;
201 this._keyDownList
[code
] = keysym
;
203 this._sendKeyEvent(keysym
, code
, true);
206 // Legacy event for browsers without code/key
207 _handleKeyPress: function (e
) {
210 // Are we expecting a keypress?
211 if (this._pendingKey
=== null) {
215 var code
= this._getKeyCode(e
);
216 var keysym
= KeyboardUtil
.getKeysym(e
);
218 // The key we were waiting for?
219 if ((code
!== 'Unidentified') && (code
!= this._pendingKey
)) {
223 code
= this._pendingKey
;
224 this._pendingKey
= null;
227 Log
.Info('keypress with no keysym:', e
);
231 this._keyDownList
[code
] = keysym
;
233 this._sendKeyEvent(keysym
, code
, true);
235 _handleKeyPressTimeout: function (e
) {
236 // Did someone manage to sort out the key already?
237 if (this._pendingKey
=== null) {
243 code
= this._pendingKey
;
244 this._pendingKey
= null;
246 // We have no way of knowing the proper keysym with the
247 // information given, but the following are true for most
249 if ((e
.keyCode
>= 0x30) && (e
.keyCode
<= 0x39)) {
252 } else if ((e
.keyCode
>= 0x41) && (e
.keyCode
<= 0x5a)) {
254 var char = String
.fromCharCode(e
.keyCode
);
255 // A feeble attempt at the correct case
257 char = char.toUpperCase();
259 char = char.toLowerCase();
260 keysym
= char.charCodeAt();
266 this._keyDownList
[code
] = keysym
;
268 this._sendKeyEvent(keysym
, code
, true);
271 _handleKeyUp: function (e
) {
274 var code
= this._getKeyCode(e
);
276 // See comment in _handleKeyDown()
277 if (isMac() && (code
=== 'CapsLock')) {
278 this._sendKeyEvent(KeyTable
.XK_Caps_Lock
, 'CapsLock', true);
279 this._sendKeyEvent(KeyTable
.XK_Caps_Lock
, 'CapsLock', false);
283 // Do we really think this key is down?
284 if (!(code
in this._keyDownList
)) {
288 this._sendKeyEvent(this._keyDownList
[code
], code
, false);
290 delete this._keyDownList
[code
];
293 _allKeysUp: function () {
294 Log
.Debug(">> Keyboard.allKeysUp");
295 for (var code
in this._keyDownList
) {
296 this._sendKeyEvent(this._keyDownList
[code
], code
, false);
298 this._keyDownList
= {};
299 Log
.Debug("<< Keyboard.allKeysUp");
302 // ===== PUBLIC METHODS =====
305 //Log.Debug(">> Keyboard.grab");
306 var c
= this._target
;
308 c
.addEventListener('keydown', this._eventHandlers
.keydown
);
309 c
.addEventListener('keyup', this._eventHandlers
.keyup
);
310 c
.addEventListener('keypress', this._eventHandlers
.keypress
);
312 // Release (key up) if window loses focus
313 window
.addEventListener('blur', this._eventHandlers
.blur
);
315 //Log.Debug("<< Keyboard.grab");
318 ungrab: function () {
319 //Log.Debug(">> Keyboard.ungrab");
320 var c
= this._target
;
322 c
.removeEventListener('keydown', this._eventHandlers
.keydown
);
323 c
.removeEventListener('keyup', this._eventHandlers
.keyup
);
324 c
.removeEventListener('keypress', this._eventHandlers
.keypress
);
325 window
.removeEventListener('blur', this._eventHandlers
.blur
);
327 // Release (key up) all keys that are in a down state
330 //Log.Debug(">> Keyboard.ungrab");