]>
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";
12 import * as browser
from "../util/browser.js";
15 // Keyboard event handler
18 export default function Keyboard(target
) {
19 this._target
= target
|| null;
21 this._keyDownList
= {}; // List of depressed keys
22 // (even if they are happy)
23 this._pendingKey
= null; // Key waiting for keypress
25 // keep these here so we can refer to them later
26 this._eventHandlers
= {
27 'keyup': this._handleKeyUp
.bind(this),
28 'keydown': this._handleKeyDown
.bind(this),
29 'keypress': this._handleKeyPress
.bind(this),
30 'blur': this._allKeysUp
.bind(this)
34 Keyboard
.prototype = {
35 // ===== EVENT HANDLERS =====
37 onkeyevent: function () {}, // Handler for key press/release
39 // ===== PRIVATE METHODS =====
41 _sendKeyEvent: function (keysym
, code
, down
) {
42 Log
.Debug("onkeyevent " + (down
? "down" : "up") +
43 ", keysym: " + keysym
, ", code: " + code
);
45 // Windows sends CtrlLeft+AltRight when you press
46 // AltGraph, which tends to confuse the hell out of
47 // remote systems. Fake a release of these keys until
48 // there is a way to detect AltGraph properly.
49 var fakeAltGraph
= false;
50 if (down
&& browser
.isWindows()) {
51 if ((code
!== 'ControlLeft') &&
52 (code
!== 'AltRight') &&
53 ('ControlLeft' in this._keyDownList
) &&
54 ('AltRight' in this._keyDownList
)) {
56 this.onkeyevent(this._keyDownList
['AltRight'],
58 this.onkeyevent(this._keyDownList
['ControlLeft'],
59 'ControlLeft', false);
63 this.onkeyevent(keysym
, code
, down
);
66 this.onkeyevent(this._keyDownList
['ControlLeft'],
68 this.onkeyevent(this._keyDownList
['AltRight'],
73 _getKeyCode: function (e
) {
74 var code
= KeyboardUtil
.getKeycode(e
);
75 if (code
!== 'Unidentified') {
79 // Unstable, but we don't have anything else to go on
80 // (don't use it for 'keypress' events thought since
81 // WebKit sets it to the same as charCode)
82 if (e
.keyCode
&& (e
.type
!== 'keypress')) {
83 // 229 is used for composition events
84 if (e
.keyCode
!== 229) {
85 return 'Platform' + e
.keyCode
;
89 // A precursor to the final DOM3 standard. Unfortunately it
90 // is not layout independent, so it is as bad as using keyCode
91 if (e
.keyIdentifier
) {
93 if (e
.keyIdentifier
.substr(0, 2) !== 'U+') {
94 return e
.keyIdentifier
;
97 var codepoint
= parseInt(e
.keyIdentifier
.substr(2), 16);
98 var char = String
.fromCharCode(codepoint
);
99 // Some implementations fail to uppercase the symbols
100 char = char.toUpperCase();
102 return 'Platform' + char.charCodeAt();
105 return 'Unidentified';
108 _handleKeyDown: function (e
) {
109 var code
= this._getKeyCode(e
);
110 var keysym
= KeyboardUtil
.getKeysym(e
);
112 // We cannot handle keys we cannot track, but we also need
113 // to deal with virtual keyboards which omit key info
114 // (iOS omits tracking info on keyup events, which forces us to
115 // special treat that platform here)
116 if ((code
=== 'Unidentified') || browser
.isIOS()) {
118 // If it's a virtual keyboard then it should be
119 // sufficient to just send press and release right
121 this._sendKeyEvent(keysym
, code
, true);
122 this._sendKeyEvent(keysym
, code
, false);
129 // Alt behaves more like AltGraph on macOS, so shuffle the
130 // keys around a bit to make things more sane for the remote
131 // server. This method is used by RealVNC and TigerVNC (and
133 if (browser
.isMac()) {
135 case KeyTable
.XK_Super_L
:
136 keysym
= KeyTable
.XK_Alt_L
;
138 case KeyTable
.XK_Super_R
:
139 keysym
= KeyTable
.XK_Super_L
;
141 case KeyTable
.XK_Alt_L
:
142 keysym
= KeyTable
.XK_Mode_switch
;
144 case KeyTable
.XK_Alt_R
:
145 keysym
= KeyTable
.XK_ISO_Level3_Shift
;
150 // Is this key already pressed? If so, then we must use the
151 // same keysym or we'll confuse the server
152 if (code
in this._keyDownList
) {
153 keysym
= this._keyDownList
[code
];
156 // macOS doesn't send proper key events for modifiers, only
157 // state change events. That gets extra confusing for CapsLock
158 // which toggles on each press, but not on release. So pretend
159 // it was a quick press and release of the button.
160 if (browser
.isMac() && (code
=== 'CapsLock')) {
161 this._sendKeyEvent(KeyTable
.XK_Caps_Lock
, 'CapsLock', true);
162 this._sendKeyEvent(KeyTable
.XK_Caps_Lock
, 'CapsLock', false);
167 // If this is a legacy browser then we'll need to wait for
168 // a keypress event as well
169 // (IE and Edge has a broken KeyboardEvent.key, so we can't
170 // just check for the presence of that field)
171 if (!keysym
&& (!e
.key
|| browser
.isIE() || browser
.isEdge())) {
172 this._pendingKey
= code
;
173 // However we might not get a keypress event if the key
174 // is non-printable, which needs some special fallback
176 setTimeout(this._handleKeyPressTimeout
.bind(this), 10, e
);
180 this._pendingKey
= null;
183 this._keyDownList
[code
] = keysym
;
185 this._sendKeyEvent(keysym
, code
, true);
188 // Legacy event for browsers without code/key
189 _handleKeyPress: function (e
) {
192 // Are we expecting a keypress?
193 if (this._pendingKey
=== null) {
197 var code
= this._getKeyCode(e
);
198 var keysym
= KeyboardUtil
.getKeysym(e
);
200 // The key we were waiting for?
201 if ((code
!== 'Unidentified') && (code
!= this._pendingKey
)) {
205 code
= this._pendingKey
;
206 this._pendingKey
= null;
209 Log
.Info('keypress with no keysym:', e
);
213 this._keyDownList
[code
] = keysym
;
215 this._sendKeyEvent(keysym
, code
, true);
217 _handleKeyPressTimeout: function (e
) {
218 // Did someone manage to sort out the key already?
219 if (this._pendingKey
=== null) {
225 code
= this._pendingKey
;
226 this._pendingKey
= null;
228 // We have no way of knowing the proper keysym with the
229 // information given, but the following are true for most
231 if ((e
.keyCode
>= 0x30) && (e
.keyCode
<= 0x39)) {
234 } else if ((e
.keyCode
>= 0x41) && (e
.keyCode
<= 0x5a)) {
236 var char = String
.fromCharCode(e
.keyCode
);
237 // A feeble attempt at the correct case
239 char = char.toUpperCase();
241 char = char.toLowerCase();
242 keysym
= char.charCodeAt();
248 this._keyDownList
[code
] = keysym
;
250 this._sendKeyEvent(keysym
, code
, true);
253 _handleKeyUp: function (e
) {
256 var code
= this._getKeyCode(e
);
258 // See comment in _handleKeyDown()
259 if (browser
.isMac() && (code
=== 'CapsLock')) {
260 this._sendKeyEvent(KeyTable
.XK_Caps_Lock
, 'CapsLock', true);
261 this._sendKeyEvent(KeyTable
.XK_Caps_Lock
, 'CapsLock', false);
265 // Do we really think this key is down?
266 if (!(code
in this._keyDownList
)) {
270 this._sendKeyEvent(this._keyDownList
[code
], code
, false);
272 delete this._keyDownList
[code
];
275 _allKeysUp: function () {
276 Log
.Debug(">> Keyboard.allKeysUp");
277 for (var code
in this._keyDownList
) {
278 this._sendKeyEvent(this._keyDownList
[code
], code
, false);
280 this._keyDownList
= {};
281 Log
.Debug("<< Keyboard.allKeysUp");
284 // ===== PUBLIC METHODS =====
287 //Log.Debug(">> Keyboard.grab");
288 var c
= this._target
;
290 c
.addEventListener('keydown', this._eventHandlers
.keydown
);
291 c
.addEventListener('keyup', this._eventHandlers
.keyup
);
292 c
.addEventListener('keypress', this._eventHandlers
.keypress
);
294 // Release (key up) if window loses focus
295 window
.addEventListener('blur', this._eventHandlers
.blur
);
297 //Log.Debug("<< Keyboard.grab");
300 ungrab: function () {
301 //Log.Debug(">> Keyboard.ungrab");
302 var c
= this._target
;
304 c
.removeEventListener('keydown', this._eventHandlers
.keydown
);
305 c
.removeEventListener('keyup', this._eventHandlers
.keyup
);
306 c
.removeEventListener('keypress', this._eventHandlers
.keypress
);
307 window
.removeEventListener('blur', this._eventHandlers
.blur
);
309 // Release (key up) all keys that are in a down state
312 //Log.Debug(">> Keyboard.ungrab");