novnc-rails 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/COPYING +0 -0
- data/LICENSE.txt +0 -0
- data/README.md +0 -0
- data/lib/novnc-rails.rb +8 -0
- data/lib/novnc-rails/version.rb +5 -0
- data/vendor/assets/javascripts/noVNC/Orbitron700.ttf +0 -0
- data/vendor/assets/javascripts/noVNC/Orbitron700.woff +0 -0
- data/vendor/assets/javascripts/noVNC/base.css +512 -0
- data/vendor/assets/javascripts/noVNC/base64.js +113 -0
- data/vendor/assets/javascripts/noVNC/black.css +71 -0
- data/vendor/assets/javascripts/noVNC/blue.css +64 -0
- data/vendor/assets/javascripts/noVNC/des.js +276 -0
- data/vendor/assets/javascripts/noVNC/display.js +751 -0
- data/vendor/assets/javascripts/noVNC/input.js +388 -0
- data/vendor/assets/javascripts/noVNC/jsunzip.js +676 -0
- data/vendor/assets/javascripts/noVNC/keyboard.js +543 -0
- data/vendor/assets/javascripts/noVNC/keysym.js +378 -0
- data/vendor/assets/javascripts/noVNC/keysymdef.js +15 -0
- data/vendor/assets/javascripts/noVNC/logo.js +1 -0
- data/vendor/assets/javascripts/noVNC/playback.js +102 -0
- data/vendor/assets/javascripts/noVNC/rfb.js +1889 -0
- data/vendor/assets/javascripts/noVNC/ui.js +979 -0
- data/vendor/assets/javascripts/noVNC/util.js +656 -0
- data/vendor/assets/javascripts/noVNC/web-socket-js/README.txt +109 -0
- data/vendor/assets/javascripts/noVNC/web-socket-js/WebSocketMain.swf +0 -0
- data/vendor/assets/javascripts/noVNC/web-socket-js/swfobject.js +4 -0
- data/vendor/assets/javascripts/noVNC/web-socket-js/web_socket.js +391 -0
- data/vendor/assets/javascripts/noVNC/websock.js +388 -0
- data/vendor/assets/javascripts/noVNC/webutil.js +239 -0
- metadata +86 -0
@@ -0,0 +1,543 @@
|
|
1
|
+
var kbdUtil = (function() {
|
2
|
+
"use strict";
|
3
|
+
|
4
|
+
function substituteCodepoint(cp) {
|
5
|
+
// Any Unicode code points which do not have corresponding keysym entries
|
6
|
+
// can be swapped out for another code point by adding them to this table
|
7
|
+
var substitutions = {
|
8
|
+
// {S,s} with comma below -> {S,s} with cedilla
|
9
|
+
0x218 : 0x15e,
|
10
|
+
0x219 : 0x15f,
|
11
|
+
// {T,t} with comma below -> {T,t} with cedilla
|
12
|
+
0x21a : 0x162,
|
13
|
+
0x21b : 0x163
|
14
|
+
};
|
15
|
+
|
16
|
+
var sub = substitutions[cp];
|
17
|
+
return sub ? sub : cp;
|
18
|
+
}
|
19
|
+
|
20
|
+
function isMac() {
|
21
|
+
return navigator && !!(/mac/i).exec(navigator.platform);
|
22
|
+
}
|
23
|
+
function isWindows() {
|
24
|
+
return navigator && !!(/win/i).exec(navigator.platform);
|
25
|
+
}
|
26
|
+
function isLinux() {
|
27
|
+
return navigator && !!(/linux/i).exec(navigator.platform);
|
28
|
+
}
|
29
|
+
|
30
|
+
// Return true if a modifier which is not the specified char modifier (and is not shift) is down
|
31
|
+
function hasShortcutModifier(charModifier, currentModifiers) {
|
32
|
+
var mods = {};
|
33
|
+
for (var key in currentModifiers) {
|
34
|
+
if (parseInt(key) !== XK_Shift_L) {
|
35
|
+
mods[key] = currentModifiers[key];
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
var sum = 0;
|
40
|
+
for (var k in currentModifiers) {
|
41
|
+
if (mods[k]) {
|
42
|
+
++sum;
|
43
|
+
}
|
44
|
+
}
|
45
|
+
if (hasCharModifier(charModifier, mods)) {
|
46
|
+
return sum > charModifier.length;
|
47
|
+
}
|
48
|
+
else {
|
49
|
+
return sum > 0;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
// Return true if the specified char modifier is currently down
|
54
|
+
function hasCharModifier(charModifier, currentModifiers) {
|
55
|
+
if (charModifier.length === 0) { return false; }
|
56
|
+
|
57
|
+
for (var i = 0; i < charModifier.length; ++i) {
|
58
|
+
if (!currentModifiers[charModifier[i]]) {
|
59
|
+
return false;
|
60
|
+
}
|
61
|
+
}
|
62
|
+
return true;
|
63
|
+
}
|
64
|
+
|
65
|
+
// Helper object tracking modifier key state
|
66
|
+
// and generates fake key events to compensate if it gets out of sync
|
67
|
+
function ModifierSync(charModifier) {
|
68
|
+
if (!charModifier) {
|
69
|
+
if (isMac()) {
|
70
|
+
// on Mac, Option (AKA Alt) is used as a char modifier
|
71
|
+
charModifier = [XK_Alt_L];
|
72
|
+
}
|
73
|
+
else if (isWindows()) {
|
74
|
+
// on Windows, Ctrl+Alt is used as a char modifier
|
75
|
+
charModifier = [XK_Alt_L, XK_Control_L];
|
76
|
+
}
|
77
|
+
else if (isLinux()) {
|
78
|
+
// on Linux, ISO Level 3 Shift (AltGr) is used as a char modifier
|
79
|
+
charModifier = [XK_ISO_Level3_Shift];
|
80
|
+
}
|
81
|
+
else {
|
82
|
+
charModifier = [];
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
var state = {};
|
87
|
+
state[XK_Control_L] = false;
|
88
|
+
state[XK_Alt_L] = false;
|
89
|
+
state[XK_ISO_Level3_Shift] = false;
|
90
|
+
state[XK_Shift_L] = false;
|
91
|
+
state[XK_Meta_L] = false;
|
92
|
+
|
93
|
+
function sync(evt, keysym) {
|
94
|
+
var result = [];
|
95
|
+
function syncKey(keysym) {
|
96
|
+
return {keysym: keysyms.lookup(keysym), type: state[keysym] ? 'keydown' : 'keyup'};
|
97
|
+
}
|
98
|
+
|
99
|
+
if (evt.ctrlKey !== undefined &&
|
100
|
+
evt.ctrlKey !== state[XK_Control_L] && keysym !== XK_Control_L) {
|
101
|
+
state[XK_Control_L] = evt.ctrlKey;
|
102
|
+
result.push(syncKey(XK_Control_L));
|
103
|
+
}
|
104
|
+
if (evt.altKey !== undefined &&
|
105
|
+
evt.altKey !== state[XK_Alt_L] && keysym !== XK_Alt_L) {
|
106
|
+
state[XK_Alt_L] = evt.altKey;
|
107
|
+
result.push(syncKey(XK_Alt_L));
|
108
|
+
}
|
109
|
+
if (evt.altGraphKey !== undefined &&
|
110
|
+
evt.altGraphKey !== state[XK_ISO_Level3_Shift] && keysym !== XK_ISO_Level3_Shift) {
|
111
|
+
state[XK_ISO_Level3_Shift] = evt.altGraphKey;
|
112
|
+
result.push(syncKey(XK_ISO_Level3_Shift));
|
113
|
+
}
|
114
|
+
if (evt.shiftKey !== undefined &&
|
115
|
+
evt.shiftKey !== state[XK_Shift_L] && keysym !== XK_Shift_L) {
|
116
|
+
state[XK_Shift_L] = evt.shiftKey;
|
117
|
+
result.push(syncKey(XK_Shift_L));
|
118
|
+
}
|
119
|
+
if (evt.metaKey !== undefined &&
|
120
|
+
evt.metaKey !== state[XK_Meta_L] && keysym !== XK_Meta_L) {
|
121
|
+
state[XK_Meta_L] = evt.metaKey;
|
122
|
+
result.push(syncKey(XK_Meta_L));
|
123
|
+
}
|
124
|
+
return result;
|
125
|
+
}
|
126
|
+
function syncKeyEvent(evt, down) {
|
127
|
+
var obj = getKeysym(evt);
|
128
|
+
var keysym = obj ? obj.keysym : null;
|
129
|
+
|
130
|
+
// first, apply the event itself, if relevant
|
131
|
+
if (keysym !== null && state[keysym] !== undefined) {
|
132
|
+
state[keysym] = down;
|
133
|
+
}
|
134
|
+
return sync(evt, keysym);
|
135
|
+
}
|
136
|
+
|
137
|
+
return {
|
138
|
+
// sync on the appropriate keyboard event
|
139
|
+
keydown: function(evt) { return syncKeyEvent(evt, true);},
|
140
|
+
keyup: function(evt) { return syncKeyEvent(evt, false);},
|
141
|
+
// Call this with a non-keyboard event (such as mouse events) to use its modifier state to synchronize anyway
|
142
|
+
syncAny: function(evt) { return sync(evt);},
|
143
|
+
|
144
|
+
// is a shortcut modifier down?
|
145
|
+
hasShortcutModifier: function() { return hasShortcutModifier(charModifier, state); },
|
146
|
+
// if a char modifier is down, return the keys it consists of, otherwise return null
|
147
|
+
activeCharModifier: function() { return hasCharModifier(charModifier, state) ? charModifier : null; }
|
148
|
+
};
|
149
|
+
}
|
150
|
+
|
151
|
+
// Get a key ID from a keyboard event
|
152
|
+
// May be a string or an integer depending on the available properties
|
153
|
+
function getKey(evt){
|
154
|
+
if ('keyCode' in evt && 'key' in evt) {
|
155
|
+
return evt.key + ':' + evt.keyCode;
|
156
|
+
}
|
157
|
+
else if ('keyCode' in evt) {
|
158
|
+
return evt.keyCode;
|
159
|
+
}
|
160
|
+
else {
|
161
|
+
return evt.key;
|
162
|
+
}
|
163
|
+
}
|
164
|
+
|
165
|
+
// Get the most reliable keysym value we can get from a key event
|
166
|
+
// if char/charCode is available, prefer those, otherwise fall back to key/keyCode/which
|
167
|
+
function getKeysym(evt){
|
168
|
+
var codepoint;
|
169
|
+
if (evt.char && evt.char.length === 1) {
|
170
|
+
codepoint = evt.char.charCodeAt();
|
171
|
+
}
|
172
|
+
else if (evt.charCode) {
|
173
|
+
codepoint = evt.charCode;
|
174
|
+
}
|
175
|
+
else if (evt.keyCode && evt.type === 'keypress') {
|
176
|
+
// IE10 stores the char code as keyCode, and has no other useful properties
|
177
|
+
codepoint = evt.keyCode;
|
178
|
+
}
|
179
|
+
if (codepoint) {
|
180
|
+
var res = keysyms.fromUnicode(substituteCodepoint(codepoint));
|
181
|
+
if (res) {
|
182
|
+
return res;
|
183
|
+
}
|
184
|
+
}
|
185
|
+
// we could check evt.key here.
|
186
|
+
// Legal values are defined in http://www.w3.org/TR/DOM-Level-3-Events/#key-values-list,
|
187
|
+
// so we "just" need to map them to keysym, but AFAIK this is only available in IE10, which also provides evt.key
|
188
|
+
// so we don't *need* it yet
|
189
|
+
if (evt.keyCode) {
|
190
|
+
return keysyms.lookup(keysymFromKeyCode(evt.keyCode, evt.shiftKey));
|
191
|
+
}
|
192
|
+
if (evt.which) {
|
193
|
+
return keysyms.lookup(keysymFromKeyCode(evt.which, evt.shiftKey));
|
194
|
+
}
|
195
|
+
return null;
|
196
|
+
}
|
197
|
+
|
198
|
+
// Given a keycode, try to predict which keysym it might be.
|
199
|
+
// If the keycode is unknown, null is returned.
|
200
|
+
function keysymFromKeyCode(keycode, shiftPressed) {
|
201
|
+
if (typeof(keycode) !== 'number') {
|
202
|
+
return null;
|
203
|
+
}
|
204
|
+
// won't be accurate for azerty
|
205
|
+
if (keycode >= 0x30 && keycode <= 0x39) {
|
206
|
+
return keycode; // digit
|
207
|
+
}
|
208
|
+
if (keycode >= 0x41 && keycode <= 0x5a) {
|
209
|
+
// remap to lowercase unless shift is down
|
210
|
+
return shiftPressed ? keycode : keycode + 32; // A-Z
|
211
|
+
}
|
212
|
+
if (keycode >= 0x60 && keycode <= 0x69) {
|
213
|
+
return XK_KP_0 + (keycode - 0x60); // numpad 0-9
|
214
|
+
}
|
215
|
+
|
216
|
+
switch(keycode) {
|
217
|
+
case 0x20: return XK_space;
|
218
|
+
case 0x6a: return XK_KP_Multiply;
|
219
|
+
case 0x6b: return XK_KP_Add;
|
220
|
+
case 0x6c: return XK_KP_Separator;
|
221
|
+
case 0x6d: return XK_KP_Subtract;
|
222
|
+
case 0x6e: return XK_KP_Decimal;
|
223
|
+
case 0x6f: return XK_KP_Divide;
|
224
|
+
case 0xbb: return XK_plus;
|
225
|
+
case 0xbc: return XK_comma;
|
226
|
+
case 0xbd: return XK_minus;
|
227
|
+
case 0xbe: return XK_period;
|
228
|
+
}
|
229
|
+
|
230
|
+
return nonCharacterKey({keyCode: keycode});
|
231
|
+
}
|
232
|
+
|
233
|
+
// if the key is a known non-character key (any key which doesn't generate character data)
|
234
|
+
// return its keysym value. Otherwise return null
|
235
|
+
function nonCharacterKey(evt) {
|
236
|
+
// evt.key not implemented yet
|
237
|
+
if (!evt.keyCode) { return null; }
|
238
|
+
var keycode = evt.keyCode;
|
239
|
+
|
240
|
+
if (keycode >= 0x70 && keycode <= 0x87) {
|
241
|
+
return XK_F1 + keycode - 0x70; // F1-F24
|
242
|
+
}
|
243
|
+
switch (keycode) {
|
244
|
+
|
245
|
+
case 8 : return XK_BackSpace;
|
246
|
+
case 13 : return XK_Return;
|
247
|
+
|
248
|
+
case 9 : return XK_Tab;
|
249
|
+
|
250
|
+
case 27 : return XK_Escape;
|
251
|
+
case 46 : return XK_Delete;
|
252
|
+
|
253
|
+
case 36 : return XK_Home;
|
254
|
+
case 35 : return XK_End;
|
255
|
+
case 33 : return XK_Page_Up;
|
256
|
+
case 34 : return XK_Page_Down;
|
257
|
+
case 45 : return XK_Insert;
|
258
|
+
|
259
|
+
case 37 : return XK_Left;
|
260
|
+
case 38 : return XK_Up;
|
261
|
+
case 39 : return XK_Right;
|
262
|
+
case 40 : return XK_Down;
|
263
|
+
|
264
|
+
case 16 : return XK_Shift_L;
|
265
|
+
case 17 : return XK_Control_L;
|
266
|
+
case 18 : return XK_Alt_L; // also: Option-key on Mac
|
267
|
+
|
268
|
+
case 224 : return XK_Meta_L;
|
269
|
+
case 225 : return XK_ISO_Level3_Shift; // AltGr
|
270
|
+
case 91 : return XK_Super_L; // also: Windows-key
|
271
|
+
case 92 : return XK_Super_R; // also: Windows-key
|
272
|
+
case 93 : return XK_Menu; // also: Windows-Menu, Command on Mac
|
273
|
+
default: return null;
|
274
|
+
}
|
275
|
+
}
|
276
|
+
return {
|
277
|
+
hasShortcutModifier : hasShortcutModifier,
|
278
|
+
hasCharModifier : hasCharModifier,
|
279
|
+
ModifierSync : ModifierSync,
|
280
|
+
getKey : getKey,
|
281
|
+
getKeysym : getKeysym,
|
282
|
+
keysymFromKeyCode : keysymFromKeyCode,
|
283
|
+
nonCharacterKey : nonCharacterKey,
|
284
|
+
substituteCodepoint : substituteCodepoint
|
285
|
+
};
|
286
|
+
})();
|
287
|
+
|
288
|
+
// Takes a DOM keyboard event and:
|
289
|
+
// - determines which keysym it represents
|
290
|
+
// - determines a keyId identifying the key that was pressed (corresponding to the key/keyCode properties on the DOM event)
|
291
|
+
// - synthesizes events to synchronize modifier key state between which modifiers are actually down, and which we thought were down
|
292
|
+
// - marks each event with an 'escape' property if a modifier was down which should be "escaped"
|
293
|
+
// - generates a "stall" event in cases where it might be necessary to wait and see if a keypress event follows a keydown
|
294
|
+
// This information is collected into an object which is passed to the next() function. (one call per event)
|
295
|
+
function KeyEventDecoder(modifierState, next) {
|
296
|
+
"use strict";
|
297
|
+
function sendAll(evts) {
|
298
|
+
for (var i = 0; i < evts.length; ++i) {
|
299
|
+
next(evts[i]);
|
300
|
+
}
|
301
|
+
}
|
302
|
+
function process(evt, type) {
|
303
|
+
var result = {type: type};
|
304
|
+
var keyId = kbdUtil.getKey(evt);
|
305
|
+
if (keyId) {
|
306
|
+
result.keyId = keyId;
|
307
|
+
}
|
308
|
+
|
309
|
+
var keysym = kbdUtil.getKeysym(evt);
|
310
|
+
|
311
|
+
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
|
312
|
+
// Is this a case where we have to decide on the keysym right away, rather than waiting for the keypress?
|
313
|
+
// "special" keys like enter, tab or backspace don't send keypress events,
|
314
|
+
// and some browsers don't send keypresses at all if a modifier is down
|
315
|
+
if (keysym && (type !== 'keydown' || kbdUtil.nonCharacterKey(evt) || hasModifier)) {
|
316
|
+
result.keysym = keysym;
|
317
|
+
}
|
318
|
+
|
319
|
+
var isShift = evt.keyCode === 0x10 || evt.key === 'Shift';
|
320
|
+
|
321
|
+
// Should we prevent the browser from handling the event?
|
322
|
+
// Doing so on a keydown (in most browsers) prevents keypress from being generated
|
323
|
+
// so only do that if we have to.
|
324
|
+
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!kbdUtil.nonCharacterKey(evt));
|
325
|
+
|
326
|
+
// If a char modifier is down on a keydown, we need to insert a stall,
|
327
|
+
// so VerifyCharModifier knows to wait and see if a keypress is comnig
|
328
|
+
var stall = type === 'keydown' && modifierState.activeCharModifier() && !kbdUtil.nonCharacterKey(evt);
|
329
|
+
|
330
|
+
// if a char modifier is pressed, get the keys it consists of (on Windows, AltGr is equivalent to Ctrl+Alt)
|
331
|
+
var active = modifierState.activeCharModifier();
|
332
|
+
|
333
|
+
// If we have a char modifier down, and we're able to determine a keysym reliably
|
334
|
+
// then (a) we know to treat the modifier as a char modifier,
|
335
|
+
// and (b) we'll have to "escape" the modifier to undo the modifier when sending the char.
|
336
|
+
if (active && keysym) {
|
337
|
+
var isCharModifier = false;
|
338
|
+
for (var i = 0; i < active.length; ++i) {
|
339
|
+
if (active[i] === keysym.keysym) {
|
340
|
+
isCharModifier = true;
|
341
|
+
}
|
342
|
+
}
|
343
|
+
if (type === 'keypress' && !isCharModifier) {
|
344
|
+
result.escape = modifierState.activeCharModifier();
|
345
|
+
}
|
346
|
+
}
|
347
|
+
|
348
|
+
if (stall) {
|
349
|
+
// insert a fake "stall" event
|
350
|
+
next({type: 'stall'});
|
351
|
+
}
|
352
|
+
next(result);
|
353
|
+
|
354
|
+
return suppress;
|
355
|
+
}
|
356
|
+
|
357
|
+
return {
|
358
|
+
keydown: function(evt) {
|
359
|
+
sendAll(modifierState.keydown(evt));
|
360
|
+
return process(evt, 'keydown');
|
361
|
+
},
|
362
|
+
keypress: function(evt) {
|
363
|
+
return process(evt, 'keypress');
|
364
|
+
},
|
365
|
+
keyup: function(evt) {
|
366
|
+
sendAll(modifierState.keyup(evt));
|
367
|
+
return process(evt, 'keyup');
|
368
|
+
},
|
369
|
+
syncModifiers: function(evt) {
|
370
|
+
sendAll(modifierState.syncAny(evt));
|
371
|
+
},
|
372
|
+
releaseAll: function() { next({type: 'releaseall'}); }
|
373
|
+
};
|
374
|
+
}
|
375
|
+
|
376
|
+
// Combines keydown and keypress events where necessary to handle char modifiers.
|
377
|
+
// On some OS'es, a char modifier is sometimes used as a shortcut modifier.
|
378
|
+
// For example, on Windows, AltGr is synonymous with Ctrl-Alt. On a Danish keyboard layout, AltGr-2 yields a @, but Ctrl-Alt-D does nothing
|
379
|
+
// so when used with the '2' key, Ctrl-Alt counts as a char modifier (and should be escaped), but when used with 'D', it does not.
|
380
|
+
// The only way we can distinguish these cases is to wait and see if a keypress event arrives
|
381
|
+
// When we receive a "stall" event, wait a few ms before processing the next keydown. If a keypress has also arrived, merge the two
|
382
|
+
function VerifyCharModifier(next) {
|
383
|
+
"use strict";
|
384
|
+
var queue = [];
|
385
|
+
var timer = null;
|
386
|
+
function process() {
|
387
|
+
if (timer) {
|
388
|
+
return;
|
389
|
+
}
|
390
|
+
|
391
|
+
var delayProcess = function () {
|
392
|
+
clearTimeout(timer);
|
393
|
+
timer = null;
|
394
|
+
process();
|
395
|
+
};
|
396
|
+
|
397
|
+
while (queue.length !== 0) {
|
398
|
+
var cur = queue[0];
|
399
|
+
queue = queue.splice(1);
|
400
|
+
switch (cur.type) {
|
401
|
+
case 'stall':
|
402
|
+
// insert a delay before processing available events.
|
403
|
+
/* jshint loopfunc: true */
|
404
|
+
timer = setTimeout(delayProcess, 5);
|
405
|
+
/* jshint loopfunc: false */
|
406
|
+
return;
|
407
|
+
case 'keydown':
|
408
|
+
// is the next element a keypress? Then we should merge the two
|
409
|
+
if (queue.length !== 0 && queue[0].type === 'keypress') {
|
410
|
+
// Firefox sends keypress even when no char is generated.
|
411
|
+
// so, if keypress keysym is the same as we'd have guessed from keydown,
|
412
|
+
// the modifier didn't have any effect, and should not be escaped
|
413
|
+
if (queue[0].escape && (!cur.keysym || cur.keysym.keysym !== queue[0].keysym.keysym)) {
|
414
|
+
cur.escape = queue[0].escape;
|
415
|
+
}
|
416
|
+
cur.keysym = queue[0].keysym;
|
417
|
+
queue = queue.splice(1);
|
418
|
+
}
|
419
|
+
break;
|
420
|
+
}
|
421
|
+
|
422
|
+
// swallow stall events, and pass all others to the next stage
|
423
|
+
if (cur.type !== 'stall') {
|
424
|
+
next(cur);
|
425
|
+
}
|
426
|
+
}
|
427
|
+
}
|
428
|
+
return function(evt) {
|
429
|
+
queue.push(evt);
|
430
|
+
process();
|
431
|
+
};
|
432
|
+
}
|
433
|
+
|
434
|
+
// Keeps track of which keys we (and the server) believe are down
|
435
|
+
// When a keyup is received, match it against this list, to determine the corresponding keysym(s)
|
436
|
+
// in some cases, a single key may produce multiple keysyms, so the corresponding keyup event must release all of these chars
|
437
|
+
// key repeat events should be merged into a single entry.
|
438
|
+
// Because we can't always identify which entry a keydown or keyup event corresponds to, we sometimes have to guess
|
439
|
+
function TrackKeyState(next) {
|
440
|
+
"use strict";
|
441
|
+
var state = [];
|
442
|
+
|
443
|
+
return function (evt) {
|
444
|
+
var last = state.length !== 0 ? state[state.length-1] : null;
|
445
|
+
|
446
|
+
switch (evt.type) {
|
447
|
+
case 'keydown':
|
448
|
+
// insert a new entry if last seen key was different.
|
449
|
+
if (!last || !evt.keyId || last.keyId !== evt.keyId) {
|
450
|
+
last = {keyId: evt.keyId, keysyms: {}};
|
451
|
+
state.push(last);
|
452
|
+
}
|
453
|
+
if (evt.keysym) {
|
454
|
+
// make sure last event contains this keysym (a single "logical" keyevent
|
455
|
+
// can cause multiple key events to be sent to the VNC server)
|
456
|
+
last.keysyms[evt.keysym.keysym] = evt.keysym;
|
457
|
+
last.ignoreKeyPress = true;
|
458
|
+
next(evt);
|
459
|
+
}
|
460
|
+
break;
|
461
|
+
case 'keypress':
|
462
|
+
if (!last) {
|
463
|
+
last = {keyId: evt.keyId, keysyms: {}};
|
464
|
+
state.push(last);
|
465
|
+
}
|
466
|
+
if (!evt.keysym) {
|
467
|
+
console.log('keypress with no keysym:', evt);
|
468
|
+
}
|
469
|
+
|
470
|
+
// If we didn't expect a keypress, and already sent a keydown to the VNC server
|
471
|
+
// based on the keydown, make sure to skip this event.
|
472
|
+
if (evt.keysym && !last.ignoreKeyPress) {
|
473
|
+
last.keysyms[evt.keysym.keysym] = evt.keysym;
|
474
|
+
evt.type = 'keydown';
|
475
|
+
next(evt);
|
476
|
+
}
|
477
|
+
break;
|
478
|
+
case 'keyup':
|
479
|
+
if (state.length === 0) {
|
480
|
+
return;
|
481
|
+
}
|
482
|
+
var idx = null;
|
483
|
+
// do we have a matching key tracked as being down?
|
484
|
+
for (var i = 0; i !== state.length; ++i) {
|
485
|
+
if (state[i].keyId === evt.keyId) {
|
486
|
+
idx = i;
|
487
|
+
break;
|
488
|
+
}
|
489
|
+
}
|
490
|
+
// if we couldn't find a match (it happens), assume it was the last key pressed
|
491
|
+
if (idx === null) {
|
492
|
+
idx = state.length - 1;
|
493
|
+
}
|
494
|
+
|
495
|
+
var item = state.splice(idx, 1)[0];
|
496
|
+
// for each keysym tracked by this key entry, clone the current event and override the keysym
|
497
|
+
var clone = (function(){
|
498
|
+
function Clone(){}
|
499
|
+
return function (obj) { Clone.prototype=obj; return new Clone(); };
|
500
|
+
}());
|
501
|
+
for (var key in item.keysyms) {
|
502
|
+
var out = clone(evt);
|
503
|
+
out.keysym = item.keysyms[key];
|
504
|
+
next(out);
|
505
|
+
}
|
506
|
+
break;
|
507
|
+
case 'releaseall':
|
508
|
+
/* jshint shadow: true */
|
509
|
+
for (var i = 0; i < state.length; ++i) {
|
510
|
+
for (var key in state[i].keysyms) {
|
511
|
+
var keysym = state[i].keysyms[key];
|
512
|
+
next({keyId: 0, keysym: keysym, type: 'keyup'});
|
513
|
+
}
|
514
|
+
}
|
515
|
+
/* jshint shadow: false */
|
516
|
+
state = [];
|
517
|
+
}
|
518
|
+
};
|
519
|
+
}
|
520
|
+
|
521
|
+
// Handles "escaping" of modifiers: if a char modifier is used to produce a keysym (such as AltGr-2 to generate an @),
|
522
|
+
// then the modifier must be "undone" before sending the @, and "redone" afterwards.
|
523
|
+
function EscapeModifiers(next) {
|
524
|
+
"use strict";
|
525
|
+
return function(evt) {
|
526
|
+
if (evt.type !== 'keydown' || evt.escape === undefined) {
|
527
|
+
next(evt);
|
528
|
+
return;
|
529
|
+
}
|
530
|
+
// undo modifiers
|
531
|
+
for (var i = 0; i < evt.escape.length; ++i) {
|
532
|
+
next({type: 'keyup', keyId: 0, keysym: keysyms.lookup(evt.escape[i])});
|
533
|
+
}
|
534
|
+
// send the character event
|
535
|
+
next(evt);
|
536
|
+
// redo modifiers
|
537
|
+
/* jshint shadow: true */
|
538
|
+
for (var i = 0; i < evt.escape.length; ++i) {
|
539
|
+
next({type: 'keydown', keyId: 0, keysym: keysyms.lookup(evt.escape[i])});
|
540
|
+
}
|
541
|
+
/* jshint shadow: false */
|
542
|
+
};
|
543
|
+
}
|