util.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import KeyTable from "./keysym.js";
  2. import keysyms from "./keysymdef.js";
  3. import vkeys from "./vkeys.js";
  4. import fixedkeys from "./fixedkeys.js";
  5. import DOMKeyTable from "./domkeytable.js";
  6. import * as browser from "../util/browser.js";
  7. // Get 'KeyboardEvent.code', handling legacy browsers
  8. export function getKeycode(evt) {
  9. // Are we getting proper key identifiers?
  10. // (unfortunately Firefox and Chrome are crappy here and gives
  11. // us an empty string on some platforms, rather than leaving it
  12. // undefined)
  13. if (evt.code) {
  14. // Mozilla isn't fully in sync with the spec yet
  15. switch (evt.code) {
  16. case 'OSLeft': return 'MetaLeft';
  17. case 'OSRight': return 'MetaRight';
  18. }
  19. return evt.code;
  20. }
  21. // The de-facto standard is to use Windows Virtual-Key codes
  22. // in the 'keyCode' field for non-printable characters
  23. if (evt.keyCode in vkeys) {
  24. let code = vkeys[evt.keyCode];
  25. // macOS has messed up this code for some reason
  26. if (browser.isMac() && (code === 'ContextMenu')) {
  27. code = 'MetaRight';
  28. }
  29. // The keyCode doesn't distinguish between left and right
  30. // for the standard modifiers
  31. if (evt.location === 2) {
  32. switch (code) {
  33. case 'ShiftLeft': return 'ShiftRight';
  34. case 'ControlLeft': return 'ControlRight';
  35. case 'AltLeft': return 'AltRight';
  36. }
  37. }
  38. // Nor a bunch of the numpad keys
  39. if (evt.location === 3) {
  40. switch (code) {
  41. case 'Delete': return 'NumpadDecimal';
  42. case 'Insert': return 'Numpad0';
  43. case 'End': return 'Numpad1';
  44. case 'ArrowDown': return 'Numpad2';
  45. case 'PageDown': return 'Numpad3';
  46. case 'ArrowLeft': return 'Numpad4';
  47. case 'ArrowRight': return 'Numpad6';
  48. case 'Home': return 'Numpad7';
  49. case 'ArrowUp': return 'Numpad8';
  50. case 'PageUp': return 'Numpad9';
  51. case 'Enter': return 'NumpadEnter';
  52. }
  53. }
  54. return code;
  55. }
  56. return 'Unidentified';
  57. }
  58. // Get 'KeyboardEvent.key', handling legacy browsers
  59. export function getKey(evt) {
  60. // Are we getting a proper key value?
  61. if ((evt.key !== undefined) && (evt.key !== 'Unidentified')) {
  62. // Mozilla isn't fully in sync with the spec yet
  63. switch (evt.key) {
  64. case 'OS': return 'Meta';
  65. case 'LaunchMyComputer': return 'LaunchApplication1';
  66. case 'LaunchCalculator': return 'LaunchApplication2';
  67. }
  68. // iOS leaks some OS names
  69. switch (evt.key) {
  70. case 'UIKeyInputUpArrow': return 'ArrowUp';
  71. case 'UIKeyInputDownArrow': return 'ArrowDown';
  72. case 'UIKeyInputLeftArrow': return 'ArrowLeft';
  73. case 'UIKeyInputRightArrow': return 'ArrowRight';
  74. case 'UIKeyInputEscape': return 'Escape';
  75. }
  76. // Broken behaviour in Chrome
  77. if ((evt.key === '\x00') && (evt.code === 'NumpadDecimal')) {
  78. return 'Delete';
  79. }
  80. return evt.key;
  81. }
  82. // Try to deduce it based on the physical key
  83. const code = getKeycode(evt);
  84. if (code in fixedkeys) {
  85. return fixedkeys[code];
  86. }
  87. // If that failed, then see if we have a printable character
  88. if (evt.charCode) {
  89. return String.fromCharCode(evt.charCode);
  90. }
  91. // At this point we have nothing left to go on
  92. return 'Unidentified';
  93. }
  94. // Get the most reliable keysym value we can get from a key event
  95. export function getKeysym(evt) {
  96. const key = getKey(evt);
  97. if (key === 'Unidentified') {
  98. return null;
  99. }
  100. // First look up special keys
  101. if (key in DOMKeyTable) {
  102. let location = evt.location;
  103. // Safari screws up location for the right cmd key
  104. if ((key === 'Meta') && (location === 0)) {
  105. location = 2;
  106. }
  107. // And for Clear
  108. if ((key === 'Clear') && (location === 3)) {
  109. let code = getKeycode(evt);
  110. if (code === 'NumLock') {
  111. location = 0;
  112. }
  113. }
  114. if ((location === undefined) || (location > 3)) {
  115. location = 0;
  116. }
  117. // The original Meta key now gets confused with the Windows key
  118. // https://bugs.chromium.org/p/chromium/issues/detail?id=1020141
  119. // https://bugzilla.mozilla.org/show_bug.cgi?id=1232918
  120. if (key === 'Meta') {
  121. let code = getKeycode(evt);
  122. if (code === 'AltLeft') {
  123. return KeyTable.XK_Meta_L;
  124. } else if (code === 'AltRight') {
  125. return KeyTable.XK_Meta_R;
  126. }
  127. }
  128. // macOS has Clear instead of NumLock, but the remote system is
  129. // probably not macOS, so lying here is probably best...
  130. if (key === 'Clear') {
  131. let code = getKeycode(evt);
  132. if (code === 'NumLock') {
  133. return KeyTable.XK_Num_Lock;
  134. }
  135. }
  136. // Windows sends alternating symbols for some keys when using a
  137. // Japanese layout. We have no way of synchronising with the IM
  138. // running on the remote system, so we send some combined keysym
  139. // instead and hope for the best.
  140. if (browser.isWindows()) {
  141. switch (key) {
  142. case 'Zenkaku':
  143. case 'Hankaku':
  144. return KeyTable.XK_Zenkaku_Hankaku;
  145. case 'Romaji':
  146. case 'KanaMode':
  147. return KeyTable.XK_Romaji;
  148. }
  149. }
  150. return DOMKeyTable[key][location];
  151. }
  152. // Now we need to look at the Unicode symbol instead
  153. // Special key? (FIXME: Should have been caught earlier)
  154. if (key.length !== 1) {
  155. return null;
  156. }
  157. const codepoint = key.charCodeAt();
  158. if (codepoint) {
  159. return keysyms.lookup(codepoint);
  160. }
  161. return null;
  162. }