You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

KeyMap.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /* Copyright (C) 2017 Brian P. Hinz
  2. *
  3. * This is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This software is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this software; if not, write to the Free Software
  15. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
  16. * USA.
  17. */
  18. package com.tigervnc.vncviewer;
  19. import java.awt.*;
  20. import java.awt.event.*;
  21. import java.util.*;
  22. import javax.swing.*;
  23. import com.tigervnc.rfb.*;
  24. import static java.awt.event.KeyEvent.*;
  25. import static com.tigervnc.rfb.Keysymdef.*;
  26. public class KeyMap {
  27. public final static int NoSymbol = 0;
  28. private static final HashMap<Integer, Character> code_map_java_to_char;
  29. static {
  30. // Certain KeyStrokes fail to produce a valid character (CTRL+ALT+...
  31. // on Windows and Mac almost never does). For these cases, the best
  32. // we can try to do is to map to the ASCII symbol from the keyCode.
  33. code_map_java_to_char = new HashMap<Integer, Character>();
  34. for (int c=32; c<0x7f; c++) {
  35. int keyCode = KeyEvent.getExtendedKeyCodeForChar(c);
  36. if (keyCode != KeyEvent.VK_UNDEFINED)
  37. // Not all ASCII characters have VK_ constants, see vk_to_ascii()
  38. code_map_java_to_char.put(keyCode, (char)c);
  39. }
  40. }
  41. private static int[][] vkey_map = {
  42. /* KEYCODE LOCATION */
  43. /* UNKNOWN STANDARD LEFT RIGHT NUMPAD */
  44. { VK_BACK_SPACE, NoSymbol, XK_BackSpace, NoSymbol, NoSymbol, NoSymbol },
  45. { VK_TAB, NoSymbol, XK_Tab, NoSymbol, NoSymbol, NoSymbol },
  46. { VK_CANCEL, NoSymbol, XK_Cancel, NoSymbol, NoSymbol, NoSymbol },
  47. { VK_ENTER, NoSymbol, XK_Return, NoSymbol, NoSymbol, XK_KP_Enter },
  48. { VK_SHIFT, NoSymbol, XK_Shift_L, XK_Shift_L, XK_Shift_R, NoSymbol },
  49. { VK_CONTROL, NoSymbol, XK_Control_L, XK_Control_L, XK_Control_R, NoSymbol },
  50. { VK_ALT, NoSymbol, XK_Alt_L, XK_Alt_L, XK_Alt_R, NoSymbol },
  51. /* VK_PAUSE left out on purpose because interpretation depends on state of CTRL. See further down. */
  52. { VK_CAPS_LOCK, NoSymbol, XK_Caps_Lock, NoSymbol, NoSymbol, NoSymbol },
  53. { VK_ESCAPE, NoSymbol, XK_Escape, NoSymbol, NoSymbol, NoSymbol },
  54. { VK_END, NoSymbol, XK_End, NoSymbol, NoSymbol, XK_KP_End },
  55. { VK_HOME, NoSymbol, XK_Home, NoSymbol, NoSymbol, XK_KP_Home },
  56. { VK_LEFT, NoSymbol, XK_Left, NoSymbol, NoSymbol, XK_KP_Left },
  57. { VK_UP, NoSymbol, XK_Up, NoSymbol, NoSymbol, XK_KP_Up },
  58. { VK_RIGHT, NoSymbol, XK_Right, NoSymbol, NoSymbol, XK_KP_Right },
  59. { VK_DOWN, NoSymbol, XK_Down, NoSymbol, NoSymbol, XK_KP_Down },
  60. /* VK_PRINTSCREEN left out on purpose because interpretation depends on state of CTRL. See further down. */
  61. { VK_PAGE_UP, NoSymbol, XK_Page_Up, NoSymbol, NoSymbol, XK_KP_Page_Up },
  62. { VK_PAGE_DOWN, NoSymbol, XK_Page_Down, NoSymbol, NoSymbol, XK_KP_Page_Down },
  63. { VK_BEGIN, NoSymbol, XK_Begin, NoSymbol, NoSymbol, XK_KP_Begin },
  64. { VK_KP_LEFT, NoSymbol, XK_KP_Left, NoSymbol, NoSymbol, XK_KP_Left },
  65. { VK_KP_UP, NoSymbol, XK_KP_Up, NoSymbol, NoSymbol, XK_KP_Up },
  66. { VK_KP_RIGHT, NoSymbol, XK_KP_Right, NoSymbol, NoSymbol, XK_KP_Right },
  67. { VK_KP_DOWN, NoSymbol, XK_KP_Down, NoSymbol, NoSymbol, XK_KP_Down },
  68. { VK_INSERT, NoSymbol, XK_Insert, NoSymbol, NoSymbol, XK_KP_Insert },
  69. { VK_DELETE, NoSymbol, XK_Delete, NoSymbol, NoSymbol, XK_KP_Delete },
  70. { VK_WINDOWS, NoSymbol, NoSymbol, XK_Super_L, XK_Super_R, NoSymbol },
  71. { VK_CONTEXT_MENU, NoSymbol, XK_Menu, NoSymbol, NoSymbol, NoSymbol },
  72. { VK_NUMPAD0, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_0 },
  73. { VK_NUMPAD1, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_1 },
  74. { VK_NUMPAD2, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_2 },
  75. { VK_NUMPAD3, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_3 },
  76. { VK_NUMPAD4, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_4 },
  77. { VK_NUMPAD5, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_5 },
  78. { VK_NUMPAD6, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_6 },
  79. { VK_NUMPAD7, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_7 },
  80. { VK_NUMPAD8, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_8 },
  81. { VK_NUMPAD9, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_9 },
  82. { VK_MULTIPLY, NoSymbol, XK_KP_Multiply, NoSymbol, NoSymbol, XK_KP_Multiply },
  83. { VK_ADD, NoSymbol, XK_KP_Add, NoSymbol, NoSymbol, XK_KP_Add },
  84. { VK_SUBTRACT, NoSymbol, XK_KP_Subtract, NoSymbol, NoSymbol, XK_KP_Subtract },
  85. { VK_DIVIDE, NoSymbol, XK_KP_Divide, NoSymbol, NoSymbol, XK_KP_Divide },
  86. { VK_SEPARATER, NoSymbol, XK_KP_Separator, NoSymbol, NoSymbol, XK_KP_Separator },
  87. { VK_DECIMAL, NoSymbol, XK_KP_Decimal, NoSymbol, NoSymbol, XK_KP_Decimal },
  88. { VK_F1, NoSymbol, XK_F1, XK_L1, XK_R1, NoSymbol },
  89. { VK_F2, NoSymbol, XK_F2, XK_L2, XK_R2, NoSymbol },
  90. { VK_F3, NoSymbol, XK_F3, XK_L3, XK_R3, NoSymbol },
  91. { VK_F4, NoSymbol, XK_F4, XK_L4, XK_R4, NoSymbol },
  92. { VK_F5, NoSymbol, XK_F5, XK_L5, XK_R5, NoSymbol },
  93. { VK_F6, NoSymbol, XK_F6, XK_L6, XK_R6, NoSymbol },
  94. { VK_F7, NoSymbol, XK_F7, XK_L7, XK_R7, NoSymbol },
  95. { VK_F8, NoSymbol, XK_F8, XK_L8, XK_R8, NoSymbol },
  96. { VK_F9, NoSymbol, XK_F9, XK_L9, XK_R9, NoSymbol },
  97. { VK_F10, NoSymbol, XK_F10, XK_L10, XK_R10, NoSymbol },
  98. { VK_F11, NoSymbol, XK_F11, NoSymbol, XK_R11, NoSymbol },
  99. { VK_F12, NoSymbol, XK_F12, NoSymbol, XK_R12, NoSymbol },
  100. { VK_F13, NoSymbol, XK_F13, NoSymbol, XK_R13, NoSymbol },
  101. { VK_F14, NoSymbol, XK_F14, NoSymbol, XK_R14, NoSymbol },
  102. { VK_F15, NoSymbol, XK_F15, NoSymbol, XK_R15, NoSymbol },
  103. { VK_F16, NoSymbol, XK_F16, NoSymbol, NoSymbol, NoSymbol },
  104. { VK_F17, NoSymbol, XK_F17, NoSymbol, NoSymbol, NoSymbol },
  105. { VK_F18, NoSymbol, XK_F18, NoSymbol, NoSymbol, NoSymbol },
  106. { VK_F19, NoSymbol, XK_F19, NoSymbol, NoSymbol, NoSymbol },
  107. { VK_F20, NoSymbol, XK_F20, NoSymbol, NoSymbol, NoSymbol },
  108. { VK_F21, NoSymbol, XK_F21, NoSymbol, NoSymbol, NoSymbol },
  109. { VK_F22, NoSymbol, XK_F22, NoSymbol, NoSymbol, NoSymbol },
  110. { VK_F23, NoSymbol, XK_F23, NoSymbol, NoSymbol, NoSymbol },
  111. { VK_F24, NoSymbol, XK_F24, NoSymbol, NoSymbol, NoSymbol },
  112. { VK_NUM_LOCK, NoSymbol, XK_Num_Lock, NoSymbol, NoSymbol, XK_Num_Lock },
  113. { VK_SCROLL_LOCK, NoSymbol, XK_Scroll_Lock, NoSymbol, NoSymbol, NoSymbol },
  114. { VK_ALT_GRAPH, NoSymbol, XK_ISO_Level3_Shift, NoSymbol, NoSymbol, NoSymbol },
  115. { VK_META, NoSymbol, NoSymbol, XK_Meta_L, XK_Meta_R, NoSymbol },
  116. { VK_MODECHANGE, NoSymbol, XK_Mode_switch, NoSymbol, NoSymbol, NoSymbol },
  117. { VK_CLEAR, NoSymbol, XK_Clear, NoSymbol, NoSymbol, XK_KP_Begin },
  118. { VK_AGAIN, NoSymbol, XK_Redo, NoSymbol, NoSymbol, NoSymbol },
  119. { VK_UNDO, NoSymbol, XK_Undo, NoSymbol, NoSymbol, NoSymbol },
  120. { VK_FIND, NoSymbol, XK_Find, NoSymbol, NoSymbol, NoSymbol },
  121. { VK_STOP, NoSymbol, XK_Cancel, NoSymbol, NoSymbol, NoSymbol },
  122. { VK_HELP, NoSymbol, XK_Help, NoSymbol, NoSymbol, NoSymbol },
  123. { VK_KANJI, NoSymbol, XK_Kanji, NoSymbol, NoSymbol, NoSymbol },
  124. { VK_KATAKANA, NoSymbol, XK_Katakana, NoSymbol, NoSymbol, NoSymbol },
  125. { VK_HIRAGANA, NoSymbol, XK_Hiragana, NoSymbol, NoSymbol, NoSymbol },
  126. { VK_PREVIOUS_CANDIDATE, NoSymbol, XK_PreviousCandidate, NoSymbol, NoSymbol, NoSymbol },
  127. { VK_CODE_INPUT, NoSymbol, XK_Codeinput, NoSymbol, NoSymbol, NoSymbol },
  128. { VK_JAPANESE_ROMAN, NoSymbol, XK_Romaji, NoSymbol, NoSymbol, NoSymbol },
  129. { VK_KANA_LOCK, NoSymbol, XK_Kana_Lock, NoSymbol, NoSymbol, NoSymbol },
  130. { VK_DEAD_ABOVEDOT, NoSymbol, XK_dead_abovedot, NoSymbol, NoSymbol, NoSymbol },
  131. { VK_DEAD_ABOVERING, NoSymbol, XK_dead_abovering, NoSymbol, NoSymbol, NoSymbol },
  132. { VK_DEAD_ACUTE, NoSymbol, XK_dead_acute, NoSymbol, NoSymbol, NoSymbol },
  133. { VK_DEAD_BREVE, NoSymbol, XK_dead_breve, NoSymbol, NoSymbol, NoSymbol },
  134. { VK_DEAD_CARON, NoSymbol, XK_dead_caron, NoSymbol, NoSymbol, NoSymbol },
  135. { VK_DEAD_CEDILLA, NoSymbol, XK_dead_cedilla, NoSymbol, NoSymbol, NoSymbol },
  136. { VK_DEAD_CIRCUMFLEX, NoSymbol, XK_dead_circumflex, NoSymbol, NoSymbol, NoSymbol },
  137. { VK_DEAD_DIAERESIS, NoSymbol, XK_dead_diaeresis, NoSymbol, NoSymbol, NoSymbol },
  138. { VK_DEAD_DOUBLEACUTE, NoSymbol, XK_dead_doubleacute, NoSymbol, NoSymbol, NoSymbol },
  139. { VK_DEAD_GRAVE, NoSymbol, XK_dead_grave, NoSymbol, NoSymbol, NoSymbol },
  140. { VK_DEAD_IOTA, NoSymbol, XK_dead_iota, NoSymbol, NoSymbol, NoSymbol },
  141. { VK_DEAD_MACRON, NoSymbol, XK_dead_macron, NoSymbol, NoSymbol, NoSymbol },
  142. { VK_DEAD_OGONEK, NoSymbol, XK_dead_ogonek, NoSymbol, NoSymbol, NoSymbol },
  143. { VK_DEAD_SEMIVOICED_SOUND, NoSymbol, XK_dead_semivoiced_sound, NoSymbol, NoSymbol, NoSymbol },
  144. { VK_DEAD_TILDE, NoSymbol, XK_dead_tilde, NoSymbol, NoSymbol, NoSymbol },
  145. { VK_DEAD_VOICED_SOUND, NoSymbol, XK_dead_voiced_sound, NoSymbol, NoSymbol, NoSymbol },
  146. { VK_ALPHANUMERIC, NoSymbol, XK_Eisu_Shift, NoSymbol, NoSymbol, NoSymbol },
  147. { VK_ALL_CANDIDATES, NoSymbol, XK_MultipleCandidate, NoSymbol, NoSymbol, NoSymbol },
  148. { VK_KANA, NoSymbol, XK_Kana_Shift, NoSymbol, NoSymbol, NoSymbol },
  149. { VK_JAPANESE_KATAKANA, NoSymbol, XK_Katakana, NoSymbol, NoSymbol, NoSymbol },
  150. { VK_JAPANESE_HIRAGANA, NoSymbol, XK_Hiragana, NoSymbol, NoSymbol, NoSymbol },
  151. { VK_COMPOSE, NoSymbol, XK_Multi_key, NoSymbol, NoSymbol, NoSymbol },
  152. };
  153. public static int vkey_to_keysym(KeyEvent ev)
  154. {
  155. int keyCode = ev.getKeyCode();
  156. // Start with keys that either don't generate a symbol, or
  157. // generate the same symbol as some other key.
  158. if (keyCode == KeyEvent.VK_PAUSE)
  159. return (ev.isControlDown() ? XK_Break : XK_Pause);
  160. else if (keyCode == KeyEvent.VK_PRINTSCREEN)
  161. return (ev.isControlDown() ? XK_Sys_Req : XK_Print);
  162. else
  163. for(int i = 0; i < vkey_map.length; i++)
  164. if (keyCode == vkey_map[i][0])
  165. return vkey_map[i][ev.getKeyLocation()+1];
  166. // Unknown special key?
  167. if (KeyEvent.getKeyText(keyCode).isEmpty()) {
  168. vlog.error("Unknown key code: 0x%04x", keyCode);
  169. return NoSymbol;
  170. }
  171. // Pressing Ctrl wreaks havoc with the symbol lookup...
  172. int ucs = (int)ev.getKeyChar();
  173. if (ev.isControlDown()) {
  174. // For CTRL-<letter>, CTRL is sent separately, so just send <letter>.
  175. if ((ucs >= 1 && ucs <= 26 && !ev.isShiftDown()) ||
  176. // CTRL-{, CTRL-|, CTRL-} also map to ASCII 96-127
  177. (ucs >= 27 && ucs <= 29 && ev.isShiftDown()))
  178. ucs += 96;
  179. // For CTRL-SHIFT-<letter>, send capital <letter> to emulate behavior
  180. // of Linux. For CTRL-@, send @. For CTRL-_, send _. For CTRL-^,
  181. // send ^.
  182. else if (ucs < 32)
  183. ucs += 64;
  184. // If it's still undefined, map the keyCode to ASCII symbol
  185. else if (keyCode >= 0 && keyCode <= 127)
  186. if (ucs == CHAR_UNDEFINED || ev.isAltDown())
  187. ucs = vk_to_ascii(keyCode, ev.isShiftDown());
  188. else if (VncViewer.os.startsWith("mac os x") && ev.isMetaDown())
  189. // Alt on OS X behaves more like AltGr on other systems, and to get
  190. // sane behaviour we should translate things in that manner for the
  191. // remote VNC server. However that means we lose the ability to use
  192. // Alt as a shortcut modifier. Do what RealVNC does and hijack the
  193. // left command key as an Alt replacement.
  194. ucs = vk_to_ascii(keyCode, ev.isShiftDown());
  195. }
  196. // Dead keys are represented by their spacing equivalent
  197. // (or something similar depending on the layout)
  198. if (Character.getType(ucs) == Character.COMBINING_SPACING_MARK)
  199. return Keysym2ucs.ucs2keysym(Keysym2ucs.ucs2combining(ucs));
  200. if (Character.isDefined(ucs))
  201. return Keysym2ucs.ucs2keysym(ucs);
  202. return NoSymbol;
  203. }
  204. private static int vk_to_ascii(int vk, boolean shift) {
  205. char c = 0;
  206. if (code_map_java_to_char.containsKey(vk))
  207. c = code_map_java_to_char.get(vk);
  208. // 0x25 (%) and 0x3F (?) do not have VK_ constants
  209. if (vk == VK_5)
  210. c = shift ? '%' : c;
  211. else if (vk == VK_SLASH)
  212. c = shift ? '?' : c;
  213. if (Character.isLetter(c))
  214. c = shift ? Character.toUpperCase(c) : Character.toLowerCase(c);
  215. return (int)c;
  216. }
  217. static LogWriter vlog = new LogWriter("KeyMap");
  218. }