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