@novnc/novnc 1.2.0 → 1.3.0-g0ef7582

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/LICENSE.txt +0 -6
  2. package/README.md +16 -6
  3. package/core/decoders/copyrect.js +5 -0
  4. package/core/decoders/hextile.js +57 -3
  5. package/core/decoders/jpeg.js +141 -0
  6. package/core/decoders/raw.js +12 -2
  7. package/core/decoders/tight.js +24 -8
  8. package/core/decoders/zrle.js +185 -0
  9. package/core/display.js +21 -151
  10. package/core/encodings.js +4 -0
  11. package/core/input/domkeytable.js +25 -21
  12. package/core/input/keyboard.js +22 -127
  13. package/core/input/util.js +18 -35
  14. package/core/input/vkeys.js +0 -1
  15. package/core/input/xtscancodes.js +5 -3
  16. package/core/ra2.js +567 -0
  17. package/core/rfb.js +487 -171
  18. package/core/util/browser.js +0 -17
  19. package/core/util/cursor.js +1 -11
  20. package/core/util/events.js +0 -4
  21. package/core/util/md5.js +79 -0
  22. package/core/websock.js +76 -17
  23. package/docs/API.md +107 -6
  24. package/docs/LIBRARY.md +3 -7
  25. package/lib/base64.js +24 -38
  26. package/lib/decoders/copyrect.js +6 -11
  27. package/lib/decoders/hextile.js +68 -44
  28. package/lib/decoders/jpeg.js +146 -0
  29. package/lib/decoders/raw.js +14 -21
  30. package/lib/decoders/rre.js +3 -17
  31. package/lib/decoders/tight.js +43 -93
  32. package/lib/decoders/tightpng.js +11 -33
  33. package/lib/decoders/zrle.js +185 -0
  34. package/lib/deflator.js +9 -26
  35. package/lib/des.js +22 -38
  36. package/lib/display.js +100 -315
  37. package/lib/encodings.js +7 -8
  38. package/lib/inflator.js +6 -22
  39. package/lib/input/domkeytable.js +240 -208
  40. package/lib/input/fixedkeys.js +10 -5
  41. package/lib/input/gesturehandler.js +84 -154
  42. package/lib/input/keyboard.js +87 -238
  43. package/lib/input/keysym.js +16 -272
  44. package/lib/input/keysymdef.js +7 -9
  45. package/lib/input/util.js +69 -156
  46. package/lib/input/vkeys.js +2 -7
  47. package/lib/input/xtscancodes.js +10 -171
  48. package/lib/ra2.js +1033 -0
  49. package/lib/rfb.js +947 -1149
  50. package/lib/util/browser.js +25 -52
  51. package/lib/util/cursor.js +25 -81
  52. package/lib/util/element.js +3 -5
  53. package/lib/util/events.js +26 -35
  54. package/lib/util/eventtarget.js +4 -16
  55. package/lib/util/int.js +2 -3
  56. package/lib/util/logging.js +3 -21
  57. package/lib/util/md5.js +83 -0
  58. package/lib/util/strings.js +3 -5
  59. package/lib/vendor/pako/lib/utils/common.js +10 -19
  60. package/lib/vendor/pako/lib/zlib/adler32.js +4 -8
  61. package/lib/vendor/pako/lib/zlib/constants.js +4 -7
  62. package/lib/vendor/pako/lib/zlib/crc32.js +6 -13
  63. package/lib/vendor/pako/lib/zlib/deflate.js +304 -708
  64. package/lib/vendor/pako/lib/zlib/gzheader.js +2 -14
  65. package/lib/vendor/pako/lib/zlib/inffast.js +61 -177
  66. package/lib/vendor/pako/lib/zlib/inflate.js +421 -909
  67. package/lib/vendor/pako/lib/zlib/inftrees.js +66 -172
  68. package/lib/vendor/pako/lib/zlib/messages.js +3 -13
  69. package/lib/vendor/pako/lib/zlib/trees.js +250 -592
  70. package/lib/vendor/pako/lib/zlib/zstream.js +3 -19
  71. package/lib/websock.js +119 -111
  72. package/package.json +2 -10
  73. package/core/util/polyfill.js +0 -61
  74. package/lib/util/polyfill.js +0 -72
  75. package/lib/vendor/promise.js +0 -255
package/core/display.js CHANGED
@@ -8,7 +8,6 @@
8
8
 
9
9
  import * as Log from './util/logging.js';
10
10
  import Base64 from "./base64.js";
11
- import { supportsImageMetadata } from './util/browser.js';
12
11
  import { toSigned32bit } from './util/int.js';
13
12
 
14
13
  export default class Display {
@@ -23,10 +22,6 @@ export default class Display {
23
22
  this._fbHeight = 0;
24
23
 
25
24
  this._prevDrawStyle = "";
26
- this._tile = null;
27
- this._tile16x16 = null;
28
- this._tileX = 0;
29
- this._tileY = 0;
30
25
 
31
26
  Log.Debug(">> Display.constructor");
32
27
 
@@ -60,12 +55,6 @@ export default class Display {
60
55
 
61
56
  Log.Debug("User Agent: " + navigator.userAgent);
62
57
 
63
- // Check canvas features
64
- if (!('createImageData' in this._drawCtx)) {
65
- throw new Error("Canvas does not support createImageData");
66
- }
67
-
68
- this._tile16x16 = this._drawCtx.createImageData(16, 16);
69
58
  Log.Debug("<< Display.constructor");
70
59
 
71
60
  // ===== PROPERTIES =====
@@ -235,6 +224,18 @@ export default class Display {
235
224
  this.viewportChangePos(0, 0);
236
225
  }
237
226
 
227
+ getImageData() {
228
+ return this._drawCtx.getImageData(0, 0, this.width, this.height);
229
+ }
230
+
231
+ toDataURL(type, encoderOptions) {
232
+ return this._backbuffer.toDataURL(type, encoderOptions);
233
+ }
234
+
235
+ toBlob(callback, type, quality) {
236
+ return this._backbuffer.toBlob(callback, type, quality);
237
+ }
238
+
238
239
  // Track what parts of the visible canvas that need updating
239
240
  _damage(x, y, w, h) {
240
241
  if (x < this._damageBounds.left) {
@@ -378,57 +379,6 @@ export default class Display {
378
379
  });
379
380
  }
380
381
 
381
- // start updating a tile
382
- startTile(x, y, width, height, color) {
383
- this._tileX = x;
384
- this._tileY = y;
385
- if (width === 16 && height === 16) {
386
- this._tile = this._tile16x16;
387
- } else {
388
- this._tile = this._drawCtx.createImageData(width, height);
389
- }
390
-
391
- const red = color[2];
392
- const green = color[1];
393
- const blue = color[0];
394
-
395
- const data = this._tile.data;
396
- for (let i = 0; i < width * height * 4; i += 4) {
397
- data[i] = red;
398
- data[i + 1] = green;
399
- data[i + 2] = blue;
400
- data[i + 3] = 255;
401
- }
402
- }
403
-
404
- // update sub-rectangle of the current tile
405
- subTile(x, y, w, h, color) {
406
- const red = color[2];
407
- const green = color[1];
408
- const blue = color[0];
409
- const xend = x + w;
410
- const yend = y + h;
411
-
412
- const data = this._tile.data;
413
- const width = this._tile.width;
414
- for (let j = y; j < yend; j++) {
415
- for (let i = x; i < xend; i++) {
416
- const p = (i + (j * width)) * 4;
417
- data[p] = red;
418
- data[p + 1] = green;
419
- data[p + 2] = blue;
420
- data[p + 3] = 255;
421
- }
422
- }
423
- }
424
-
425
- // draw the current tile to the screen
426
- finishTile() {
427
- this._drawCtx.putImageData(this._tile, this._tileX, this._tileY);
428
- this._damage(this._tileX, this._tileY,
429
- this._tile.width, this._tile.height);
430
- }
431
-
432
382
  blitImage(x, y, width, height, arr, offset, fromQueue) {
433
383
  if (this._renderQ.length !== 0 && !fromQueue) {
434
384
  // NB(directxman12): it's technically more performant here to use preallocated arrays,
@@ -445,47 +395,13 @@ export default class Display {
445
395
  'height': height,
446
396
  });
447
397
  } else {
448
- this._bgrxImageData(x, y, width, height, arr, offset);
449
- }
450
- }
451
-
452
- blitRgbImage(x, y, width, height, arr, offset, fromQueue) {
453
- if (this._renderQ.length !== 0 && !fromQueue) {
454
- // NB(directxman12): it's technically more performant here to use preallocated arrays,
455
- // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
456
- // this probably isn't getting called *nearly* as much
457
- const newArr = new Uint8Array(width * height * 3);
458
- newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));
459
- this._renderQPush({
460
- 'type': 'blitRgb',
461
- 'data': newArr,
462
- 'x': x,
463
- 'y': y,
464
- 'width': width,
465
- 'height': height,
466
- });
467
- } else {
468
- this._rgbImageData(x, y, width, height, arr, offset);
469
- }
470
- }
471
-
472
- blitRgbxImage(x, y, width, height, arr, offset, fromQueue) {
473
- if (this._renderQ.length !== 0 && !fromQueue) {
474
- // NB(directxman12): it's technically more performant here to use preallocated arrays,
475
- // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
476
- // this probably isn't getting called *nearly* as much
477
- const newArr = new Uint8Array(width * height * 4);
478
- newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));
479
- this._renderQPush({
480
- 'type': 'blitRgbx',
481
- 'data': newArr,
482
- 'x': x,
483
- 'y': y,
484
- 'width': width,
485
- 'height': height,
486
- });
487
- } else {
488
- this._rgbxImageData(x, y, width, height, arr, offset);
398
+ // NB(directxman12): arr must be an Type Array view
399
+ let data = new Uint8ClampedArray(arr.buffer,
400
+ arr.byteOffset + offset,
401
+ width * height * 4);
402
+ let img = new ImageData(data, width, height);
403
+ this._drawCtx.putImageData(img, x, y);
404
+ this._damage(x, y, width, height);
489
405
  }
490
406
  }
491
407
 
@@ -537,52 +453,13 @@ export default class Display {
537
453
  }
538
454
 
539
455
  _setFillColor(color) {
540
- const newStyle = 'rgb(' + color[2] + ',' + color[1] + ',' + color[0] + ')';
456
+ const newStyle = 'rgb(' + color[0] + ',' + color[1] + ',' + color[2] + ')';
541
457
  if (newStyle !== this._prevDrawStyle) {
542
458
  this._drawCtx.fillStyle = newStyle;
543
459
  this._prevDrawStyle = newStyle;
544
460
  }
545
461
  }
546
462
 
547
- _rgbImageData(x, y, width, height, arr, offset) {
548
- const img = this._drawCtx.createImageData(width, height);
549
- const data = img.data;
550
- for (let i = 0, j = offset; i < width * height * 4; i += 4, j += 3) {
551
- data[i] = arr[j];
552
- data[i + 1] = arr[j + 1];
553
- data[i + 2] = arr[j + 2];
554
- data[i + 3] = 255; // Alpha
555
- }
556
- this._drawCtx.putImageData(img, x, y);
557
- this._damage(x, y, img.width, img.height);
558
- }
559
-
560
- _bgrxImageData(x, y, width, height, arr, offset) {
561
- const img = this._drawCtx.createImageData(width, height);
562
- const data = img.data;
563
- for (let i = 0, j = offset; i < width * height * 4; i += 4, j += 4) {
564
- data[i] = arr[j + 2];
565
- data[i + 1] = arr[j + 1];
566
- data[i + 2] = arr[j];
567
- data[i + 3] = 255; // Alpha
568
- }
569
- this._drawCtx.putImageData(img, x, y);
570
- this._damage(x, y, img.width, img.height);
571
- }
572
-
573
- _rgbxImageData(x, y, width, height, arr, offset) {
574
- // NB(directxman12): arr must be an Type Array view
575
- let img;
576
- if (supportsImageMetadata) {
577
- img = new ImageData(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4), width, height);
578
- } else {
579
- img = this._drawCtx.createImageData(width, height);
580
- img.data.set(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4));
581
- }
582
- this._drawCtx.putImageData(img, x, y);
583
- this._damage(x, y, img.width, img.height);
584
- }
585
-
586
463
  _renderQPush(action) {
587
464
  this._renderQ.push(action);
588
465
  if (this._renderQ.length === 1) {
@@ -616,15 +493,8 @@ export default class Display {
616
493
  case 'blit':
617
494
  this.blitImage(a.x, a.y, a.width, a.height, a.data, 0, true);
618
495
  break;
619
- case 'blitRgb':
620
- this.blitRgbImage(a.x, a.y, a.width, a.height, a.data, 0, true);
621
- break;
622
- case 'blitRgbx':
623
- this.blitRgbxImage(a.x, a.y, a.width, a.height, a.data, 0, true);
624
- break;
625
496
  case 'img':
626
- /* IE tends to set "complete" prematurely, so check dimensions */
627
- if (a.img.complete && (a.img.width !== 0) && (a.img.height !== 0)) {
497
+ if (a.img.complete) {
628
498
  if (a.img.width !== a.width || a.img.height !== a.height) {
629
499
  Log.Error("Decoded image has incorrect dimensions. Got " +
630
500
  a.img.width + "x" + a.img.height + ". Expected " +
package/core/encodings.js CHANGED
@@ -12,7 +12,9 @@ export const encodings = {
12
12
  encodingRRE: 2,
13
13
  encodingHextile: 5,
14
14
  encodingTight: 7,
15
+ encodingZRLE: 16,
15
16
  encodingTightPNG: -260,
17
+ encodingJPEG: 21,
16
18
 
17
19
  pseudoEncodingQualityLevel9: -23,
18
20
  pseudoEncodingQualityLevel0: -32,
@@ -38,7 +40,9 @@ export function encodingName(num) {
38
40
  case encodings.encodingRRE: return "RRE";
39
41
  case encodings.encodingHextile: return "Hextile";
40
42
  case encodings.encodingTight: return "Tight";
43
+ case encodings.encodingZRLE: return "ZRLE";
41
44
  case encodings.encodingTightPNG: return "TightPNG";
45
+ case encodings.encodingJPEG: return "JPEG";
42
46
  default: return "[unknown encoding " + num + "]";
43
47
  }
44
48
  }
@@ -35,7 +35,7 @@ function addNumpad(key, standard, numpad) {
35
35
  DOMKeyTable[key] = [standard, standard, standard, numpad];
36
36
  }
37
37
 
38
- // 2.2. Modifier Keys
38
+ // 3.2. Modifier Keys
39
39
 
40
40
  addLeftRight("Alt", KeyTable.XK_Alt_L, KeyTable.XK_Alt_R);
41
41
  addStandard("AltGraph", KeyTable.XK_ISO_Level3_Shift);
@@ -49,25 +49,27 @@ addStandard("ScrollLock", KeyTable.XK_Scroll_Lock);
49
49
  addLeftRight("Shift", KeyTable.XK_Shift_L, KeyTable.XK_Shift_R);
50
50
  // - Symbol
51
51
  // - SymbolLock
52
+ // - Hyper
53
+ // - Super
52
54
 
53
- // 2.3. Whitespace Keys
55
+ // 3.3. Whitespace Keys
54
56
 
55
57
  addNumpad("Enter", KeyTable.XK_Return, KeyTable.XK_KP_Enter);
56
58
  addStandard("Tab", KeyTable.XK_Tab);
57
59
  addNumpad(" ", KeyTable.XK_space, KeyTable.XK_KP_Space);
58
60
 
59
- // 2.4. Navigation Keys
61
+ // 3.4. Navigation Keys
60
62
 
61
63
  addNumpad("ArrowDown", KeyTable.XK_Down, KeyTable.XK_KP_Down);
62
- addNumpad("ArrowUp", KeyTable.XK_Up, KeyTable.XK_KP_Up);
63
64
  addNumpad("ArrowLeft", KeyTable.XK_Left, KeyTable.XK_KP_Left);
64
65
  addNumpad("ArrowRight", KeyTable.XK_Right, KeyTable.XK_KP_Right);
66
+ addNumpad("ArrowUp", KeyTable.XK_Up, KeyTable.XK_KP_Up);
65
67
  addNumpad("End", KeyTable.XK_End, KeyTable.XK_KP_End);
66
68
  addNumpad("Home", KeyTable.XK_Home, KeyTable.XK_KP_Home);
67
69
  addNumpad("PageDown", KeyTable.XK_Next, KeyTable.XK_KP_Next);
68
70
  addNumpad("PageUp", KeyTable.XK_Prior, KeyTable.XK_KP_Prior);
69
71
 
70
- // 2.5. Editing Keys
72
+ // 3.5. Editing Keys
71
73
 
72
74
  addStandard("Backspace", KeyTable.XK_BackSpace);
73
75
  // Browsers send "Clear" for the numpad 5 without NumLock because
@@ -85,7 +87,7 @@ addStandard("Paste", KeyTable.XF86XK_Paste);
85
87
  addStandard("Redo", KeyTable.XK_Redo);
86
88
  addStandard("Undo", KeyTable.XK_Undo);
87
89
 
88
- // 2.6. UI Keys
90
+ // 3.6. UI Keys
89
91
 
90
92
  // - Accept
91
93
  // - Again (could just be XK_Redo)
@@ -103,7 +105,7 @@ addStandard("Select", KeyTable.XK_Select);
103
105
  addStandard("ZoomIn", KeyTable.XF86XK_ZoomIn);
104
106
  addStandard("ZoomOut", KeyTable.XF86XK_ZoomOut);
105
107
 
106
- // 2.7. Device Keys
108
+ // 3.7. Device Keys
107
109
 
108
110
  addStandard("BrightnessDown", KeyTable.XF86XK_MonBrightnessDown);
109
111
  addStandard("BrightnessUp", KeyTable.XF86XK_MonBrightnessUp);
@@ -116,10 +118,10 @@ addStandard("Hibernate", KeyTable.XF86XK_Hibernate);
116
118
  addStandard("Standby", KeyTable.XF86XK_Standby);
117
119
  addStandard("WakeUp", KeyTable.XF86XK_WakeUp);
118
120
 
119
- // 2.8. IME and Composition Keys
121
+ // 3.8. IME and Composition Keys
120
122
 
121
123
  addStandard("AllCandidates", KeyTable.XK_MultipleCandidate);
122
- addStandard("Alphanumeric", KeyTable.XK_Eisu_Shift); // could also be _Eisu_Toggle
124
+ addStandard("Alphanumeric", KeyTable.XK_Eisu_toggle);
123
125
  addStandard("CodeInput", KeyTable.XK_Codeinput);
124
126
  addStandard("Compose", KeyTable.XK_Multi_key);
125
127
  addStandard("Convert", KeyTable.XK_Henkan);
@@ -137,7 +139,7 @@ addStandard("PreviousCandidate", KeyTable.XK_PreviousCandidate);
137
139
  addStandard("SingleCandidate", KeyTable.XK_SingleCandidate);
138
140
  addStandard("HangulMode", KeyTable.XK_Hangul);
139
141
  addStandard("HanjaMode", KeyTable.XK_Hangul_Hanja);
140
- addStandard("JunjuaMode", KeyTable.XK_Hangul_Jeonja);
142
+ addStandard("JunjaMode", KeyTable.XK_Hangul_Jeonja);
141
143
  addStandard("Eisu", KeyTable.XK_Eisu_toggle);
142
144
  addStandard("Hankaku", KeyTable.XK_Hankaku);
143
145
  addStandard("Hiragana", KeyTable.XK_Hiragana);
@@ -147,9 +149,9 @@ addStandard("KanjiMode", KeyTable.XK_Kanji);
147
149
  addStandard("Katakana", KeyTable.XK_Katakana);
148
150
  addStandard("Romaji", KeyTable.XK_Romaji);
149
151
  addStandard("Zenkaku", KeyTable.XK_Zenkaku);
150
- addStandard("ZenkakuHanaku", KeyTable.XK_Zenkaku_Hankaku);
152
+ addStandard("ZenkakuHankaku", KeyTable.XK_Zenkaku_Hankaku);
151
153
 
152
- // 2.9. General-Purpose Function Keys
154
+ // 3.9. General-Purpose Function Keys
153
155
 
154
156
  addStandard("F1", KeyTable.XK_F1);
155
157
  addStandard("F2", KeyTable.XK_F2);
@@ -188,7 +190,7 @@ addStandard("F34", KeyTable.XK_F34);
188
190
  addStandard("F35", KeyTable.XK_F35);
189
191
  // - Soft1...
190
192
 
191
- // 2.10. Multimedia Keys
193
+ // 3.10. Multimedia Keys
192
194
 
193
195
  // - ChannelDown
194
196
  // - ChannelUp
@@ -200,6 +202,7 @@ addStandard("MailSend", KeyTable.XF86XK_Send);
200
202
  addStandard("MediaFastForward", KeyTable.XF86XK_AudioForward);
201
203
  addStandard("MediaPause", KeyTable.XF86XK_AudioPause);
202
204
  addStandard("MediaPlay", KeyTable.XF86XK_AudioPlay);
205
+ // - MediaPlayPause
203
206
  addStandard("MediaRecord", KeyTable.XF86XK_AudioRecord);
204
207
  addStandard("MediaRewind", KeyTable.XF86XK_AudioRewind);
205
208
  addStandard("MediaStop", KeyTable.XF86XK_AudioStop);
@@ -211,12 +214,12 @@ addStandard("Print", KeyTable.XK_Print);
211
214
  addStandard("Save", KeyTable.XF86XK_Save);
212
215
  addStandard("SpellCheck", KeyTable.XF86XK_Spell);
213
216
 
214
- // 2.11. Multimedia Numpad Keys
217
+ // 3.11. Multimedia Numpad Keys
215
218
 
216
219
  // - Key11
217
220
  // - Key12
218
221
 
219
- // 2.12. Audio Keys
222
+ // 3.12. Audio Keys
220
223
 
221
224
  // - AudioBalanceLeft
222
225
  // - AudioBalanceRight
@@ -236,16 +239,17 @@ addStandard("AudioVolumeMute", KeyTable.XF86XK_AudioMute);
236
239
  // - MicrophoneVolumeUp
237
240
  addStandard("MicrophoneVolumeMute", KeyTable.XF86XK_AudioMicMute);
238
241
 
239
- // 2.13. Speech Keys
242
+ // 3.13. Speech Keys
240
243
 
241
244
  // - SpeechCorrectionList
242
245
  // - SpeechInputToggle
243
246
 
244
- // 2.14. Application Keys
247
+ // 3.14. Application Keys
245
248
 
246
249
  addStandard("LaunchApplication1", KeyTable.XF86XK_MyComputer);
247
250
  addStandard("LaunchApplication2", KeyTable.XF86XK_Calculator);
248
251
  addStandard("LaunchCalendar", KeyTable.XF86XK_Calendar);
252
+ // - LaunchContacts
249
253
  addStandard("LaunchMail", KeyTable.XF86XK_Mail);
250
254
  addStandard("LaunchMediaPlayer", KeyTable.XF86XK_AudioMedia);
251
255
  addStandard("LaunchMusicPlayer", KeyTable.XF86XK_Music);
@@ -256,7 +260,7 @@ addStandard("LaunchWebBrowser", KeyTable.XF86XK_WWW);
256
260
  addStandard("LaunchWebCam", KeyTable.XF86XK_WebCam);
257
261
  addStandard("LaunchWordProcessor", KeyTable.XF86XK_Word);
258
262
 
259
- // 2.15. Browser Keys
263
+ // 3.15. Browser Keys
260
264
 
261
265
  addStandard("BrowserBack", KeyTable.XF86XK_Back);
262
266
  addStandard("BrowserFavorites", KeyTable.XF86XK_Favorites);
@@ -266,15 +270,15 @@ addStandard("BrowserRefresh", KeyTable.XF86XK_Refresh);
266
270
  addStandard("BrowserSearch", KeyTable.XF86XK_Search);
267
271
  addStandard("BrowserStop", KeyTable.XF86XK_Stop);
268
272
 
269
- // 2.16. Mobile Phone Keys
273
+ // 3.16. Mobile Phone Keys
270
274
 
271
275
  // - A whole bunch...
272
276
 
273
- // 2.17. TV Keys
277
+ // 3.17. TV Keys
274
278
 
275
279
  // - A whole bunch...
276
280
 
277
- // 2.18. Media Controller Keys
281
+ // 3.18. Media Controller Keys
278
282
 
279
283
  // - A whole bunch...
280
284
  addStandard("Dimmer", KeyTable.XF86XK_BrightnessAdjust);
@@ -20,16 +20,13 @@ export default class Keyboard {
20
20
 
21
21
  this._keyDownList = {}; // List of depressed keys
22
22
  // (even if they are happy)
23
- this._pendingKey = null; // Key waiting for keypress
24
23
  this._altGrArmed = false; // Windows AltGr detection
25
24
 
26
25
  // keep these here so we can refer to them later
27
26
  this._eventHandlers = {
28
27
  'keyup': this._handleKeyUp.bind(this),
29
28
  'keydown': this._handleKeyDown.bind(this),
30
- 'keypress': this._handleKeyPress.bind(this),
31
29
  'blur': this._allKeysUp.bind(this),
32
- 'checkalt': this._checkAlt.bind(this),
33
30
  };
34
31
 
35
32
  // ===== EVENT HANDLERS =====
@@ -62,9 +59,7 @@ export default class Keyboard {
62
59
  }
63
60
 
64
61
  // Unstable, but we don't have anything else to go on
65
- // (don't use it for 'keypress' events thought since
66
- // WebKit sets it to the same as charCode)
67
- if (e.keyCode && (e.type !== 'keypress')) {
62
+ if (e.keyCode) {
68
63
  // 229 is used for composition events
69
64
  if (e.keyCode !== 229) {
70
65
  return 'Platform' + e.keyCode;
@@ -158,6 +153,16 @@ export default class Keyboard {
158
153
  keysym = this._keyDownList[code];
159
154
  }
160
155
 
156
+ // macOS doesn't send proper key releases if a key is pressed
157
+ // while meta is held down
158
+ if ((browser.isMac() || browser.isIOS()) &&
159
+ (e.metaKey && code !== 'MetaLeft' && code !== 'MetaRight')) {
160
+ this._sendKeyEvent(keysym, code, true);
161
+ this._sendKeyEvent(keysym, code, false);
162
+ stopEvent(e);
163
+ return;
164
+ }
165
+
161
166
  // macOS doesn't send proper key events for modifiers, only
162
167
  // state change events. That gets extra confusing for CapsLock
163
168
  // which toggles on each press, but not on release. So pretend
@@ -169,20 +174,20 @@ export default class Keyboard {
169
174
  return;
170
175
  }
171
176
 
172
- // If this is a legacy browser then we'll need to wait for
173
- // a keypress event as well
174
- // (IE and Edge has a broken KeyboardEvent.key, so we can't
175
- // just check for the presence of that field)
176
- if (!keysym && (!e.key || browser.isIE() || browser.isEdge())) {
177
- this._pendingKey = code;
178
- // However we might not get a keypress event if the key
179
- // is non-printable, which needs some special fallback
180
- // handling
181
- setTimeout(this._handleKeyPressTimeout.bind(this), 10, e);
177
+ // Windows doesn't send proper key releases for a bunch of
178
+ // Japanese IM keys so we have to fake the release right away
179
+ const jpBadKeys = [ KeyTable.XK_Zenkaku_Hankaku,
180
+ KeyTable.XK_Eisu_toggle,
181
+ KeyTable.XK_Katakana,
182
+ KeyTable.XK_Hiragana,
183
+ KeyTable.XK_Romaji ];
184
+ if (browser.isWindows() && jpBadKeys.includes(keysym)) {
185
+ this._sendKeyEvent(keysym, code, true);
186
+ this._sendKeyEvent(keysym, code, false);
187
+ stopEvent(e);
182
188
  return;
183
189
  }
184
190
 
185
- this._pendingKey = null;
186
191
  stopEvent(e);
187
192
 
188
193
  // Possible start of AltGr sequence? (see above)
@@ -197,69 +202,6 @@ export default class Keyboard {
197
202
  this._sendKeyEvent(keysym, code, true);
198
203
  }
199
204
 
200
- // Legacy event for browsers without code/key
201
- _handleKeyPress(e) {
202
- stopEvent(e);
203
-
204
- // Are we expecting a keypress?
205
- if (this._pendingKey === null) {
206
- return;
207
- }
208
-
209
- let code = this._getKeyCode(e);
210
- const keysym = KeyboardUtil.getKeysym(e);
211
-
212
- // The key we were waiting for?
213
- if ((code !== 'Unidentified') && (code != this._pendingKey)) {
214
- return;
215
- }
216
-
217
- code = this._pendingKey;
218
- this._pendingKey = null;
219
-
220
- if (!keysym) {
221
- Log.Info('keypress with no keysym:', e);
222
- return;
223
- }
224
-
225
- this._sendKeyEvent(keysym, code, true);
226
- }
227
-
228
- _handleKeyPressTimeout(e) {
229
- // Did someone manage to sort out the key already?
230
- if (this._pendingKey === null) {
231
- return;
232
- }
233
-
234
- let keysym;
235
-
236
- const code = this._pendingKey;
237
- this._pendingKey = null;
238
-
239
- // We have no way of knowing the proper keysym with the
240
- // information given, but the following are true for most
241
- // layouts
242
- if ((e.keyCode >= 0x30) && (e.keyCode <= 0x39)) {
243
- // Digit
244
- keysym = e.keyCode;
245
- } else if ((e.keyCode >= 0x41) && (e.keyCode <= 0x5a)) {
246
- // Character (A-Z)
247
- let char = String.fromCharCode(e.keyCode);
248
- // A feeble attempt at the correct case
249
- if (e.shiftKey) {
250
- char = char.toUpperCase();
251
- } else {
252
- char = char.toLowerCase();
253
- }
254
- keysym = char.charCodeAt();
255
- } else {
256
- // Unknown, give up
257
- keysym = 0;
258
- }
259
-
260
- this._sendKeyEvent(keysym, code, true);
261
- }
262
-
263
205
  _handleKeyUp(e) {
264
206
  stopEvent(e);
265
207
 
@@ -312,30 +254,6 @@ export default class Keyboard {
312
254
  Log.Debug("<< Keyboard.allKeysUp");
313
255
  }
314
256
 
315
- // Alt workaround for Firefox on Windows, see below
316
- _checkAlt(e) {
317
- if (e.skipCheckAlt) {
318
- return;
319
- }
320
- if (e.altKey) {
321
- return;
322
- }
323
-
324
- const target = this._target;
325
- const downList = this._keyDownList;
326
- ['AltLeft', 'AltRight'].forEach((code) => {
327
- if (!(code in downList)) {
328
- return;
329
- }
330
-
331
- const event = new KeyboardEvent('keyup',
332
- { key: downList[code],
333
- code: code });
334
- event.skipCheckAlt = true;
335
- target.dispatchEvent(event);
336
- });
337
- }
338
-
339
257
  // ===== PUBLIC METHODS =====
340
258
 
341
259
  grab() {
@@ -343,41 +261,18 @@ export default class Keyboard {
343
261
 
344
262
  this._target.addEventListener('keydown', this._eventHandlers.keydown);
345
263
  this._target.addEventListener('keyup', this._eventHandlers.keyup);
346
- this._target.addEventListener('keypress', this._eventHandlers.keypress);
347
264
 
348
265
  // Release (key up) if window loses focus
349
266
  window.addEventListener('blur', this._eventHandlers.blur);
350
267
 
351
- // Firefox on Windows has broken handling of Alt, so we need to
352
- // poll as best we can for releases (still doesn't prevent the
353
- // menu from popping up though as we can't call
354
- // preventDefault())
355
- if (browser.isWindows() && browser.isFirefox()) {
356
- const handler = this._eventHandlers.checkalt;
357
- ['mousedown', 'mouseup', 'mousemove', 'wheel',
358
- 'touchstart', 'touchend', 'touchmove',
359
- 'keydown', 'keyup'].forEach(type =>
360
- document.addEventListener(type, handler,
361
- { capture: true,
362
- passive: true }));
363
- }
364
-
365
268
  //Log.Debug("<< Keyboard.grab");
366
269
  }
367
270
 
368
271
  ungrab() {
369
272
  //Log.Debug(">> Keyboard.ungrab");
370
273
 
371
- if (browser.isWindows() && browser.isFirefox()) {
372
- const handler = this._eventHandlers.checkalt;
373
- ['mousedown', 'mouseup', 'mousemove', 'wheel',
374
- 'touchstart', 'touchend', 'touchmove',
375
- 'keydown', 'keyup'].forEach(type => document.removeEventListener(type, handler));
376
- }
377
-
378
274
  this._target.removeEventListener('keydown', this._eventHandlers.keydown);
379
275
  this._target.removeEventListener('keyup', this._eventHandlers.keyup);
380
- this._target.removeEventListener('keypress', this._eventHandlers.keypress);
381
276
  window.removeEventListener('blur', this._eventHandlers.blur);
382
277
 
383
278
  // Release (key up) all keys that are in a down state