summaryrefslogtreecommitdiffstats
path: root/java/com/tigervnc/rfb/Keysyms.java
blob: e5ece69cc747cb3a0d0fc8cdd7d4c919a0990767 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
 * Copyright (C) 2012-2013 D. R. Commander.  All Rights Reserved.
 * Copyright (C) 2013 Brian P. Hinz
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 * USA.
 */

//
// Keysyms - defines X keysyms for non-character keys.  All keysyms
// corresponding to characters should be generated by calling
// UnicodeToKeysym.ucs2keysym().
//

package com.tigervnc.rfb;

import java.awt.event.KeyEvent;
import com.tigervnc.vncviewer.VncViewer;

public class Keysyms {

  public static final int VoidSymbol = 0xffffff;
  /*
   * TTY Functions, cleverly chosen to map to ascii, for convenience of
   * programming, but could have been arbitrary (at the cost of lookup
   * tables in client code.
   */
  public static final int BackSpace = 0xFF08;
  public static final int Tab = 0xFF09;
  public static final int Linefeed = 0xFF0A;
  public static final int Clear = 0xFF0B;
  public static final int Return = 0xFF0D;
  public static final int Pause = 0xFF13;
  public static final int Scroll_Lock = 0xFF14;
  public static final int Sys_Req = 0xFF15;
  public static final int Escape = 0xFF1B;
  public static final int Delete = 0xFFFF;

  /* International & multi-key character composition */
  public static final int Multi_key = 0xFF20;  /* Multi-key character compose */
  public static final int Codeinput = 0xFF37;
  public static final int SingleCandidate = 0xFF3C;
  public static final int MultipleCandidate = 0xFF3D;
  public static final int PreviousCandidate = 0xFF3E;

  /* Japanese keyboard support */
  public static final int Kanji = 0xFF21;  /* Kanji, Kanji convert */
  public static final int Muhenkan = 0xFF22;  /* Cancel Conversion */
  public static final int Henkan_Mode = 0xFF23;  /* Start/Stop Conversion */
  public static final int Henkan = 0xFF23;  /* Alias for Henkan_Mode */
  public static final int Romaji = 0xFF24;  /* to Romaji */
  public static final int Hiragana = 0xFF25;  /* to Hiragana */
  public static final int Katakana = 0xFF26;  /* to Katakana */
  public static final int Hiragana_Katakana = 0xFF27;  /* Hiragana/Katakana toggle */
  public static final int Zenkaku = 0xFF28;  /* to Zenkaku */
  public static final int Hankaku = 0xFF29;  /* to Hankaku */
  public static final int Zenkaku_Hankaku = 0xFF2A;  /* Zenkaku/Hankaku toggle */
  public static final int Touroku = 0xFF2B;  /* Add to Dictionary */
  public static final int Massyo = 0xFF2C;  /* Delete from Dictionary */
  public static final int Kana_Lock = 0xFF2D;  /* Kana Lock */
  public static final int Kana_Shift = 0xFF2E;  /* Kana Shift */
  public static final int Eisu_Shift = 0xFF2F;  /* Alphanumeric Shift */
  public static final int Eisu_toggle = 0xFF30;  /* Alphanumeric toggle */
  public static final int Kanji_Bangou = 0xFF37;  /* Codeinput */
  public static final int Zen_Koho = 0xFF3D;  /* Multiple/All Candidate(s) */
  public static final int Mae_Koho = 0xFF3E;  /* Previous Candidate */

  /* Cursor control & motion */
  public static final int Home = 0xFF50;
  public static final int Left = 0xFF51;
  public static final int Up = 0xFF52;
  public static final int Right = 0xFF53;
  public static final int Down = 0xFF54;
  public static final int Prior = 0xFF55;
  public static final int Page_Up = 0xFF55;
  public static final int Next = 0xFF56;
  public static final int Page_Down = 0xFF56;
  public static final int End = 0xFF57;
  public static final int Begin = 0xFF58;

  /* Misc Functions */
  public static final int Select = 0xFF60;
  public static final int Print = 0xFF61;
  public static final int Execute = 0xFF62;
  public static final int Insert = 0xFF63;
  public static final int Undo = 0xFF65;
  public static final int Redo = 0xFF66;
  public static final int Menu = 0xFF67;
  public static final int Find = 0xFF68;
  public static final int Cancel = 0xFF69;
  public static final int Help = 0xFF6A;
  public static final int Break = 0xFF6B;
  public static final int Mode_switch = 0xFF7E;
  public static final int script_switch = 0xFF7E;
  public static final int Num_Lock = 0xFF7F;

  /* Keypad Functions, keypad numbers cleverly chosen to map to ascii */
  public static final int KP_Enter = 0xFF8D;
  public static final int KP_Home = 0xFF95;
  public static final int KP_Left = 0xFF96;
  public static final int KP_Up = 0xFF97;
  public static final int KP_Right = 0xFF98;
  public static final int KP_Down = 0xFF99;
  public static final int KP_Page_Up = 0xFF9A;
  public static final int KP_Page_Down = 0xFF9B;
  public static final int KP_End = 0xFF9C;
  public static final int KP_Begin = 0xFF9D;
  public static final int KP_Insert = 0xFF9E;
  public static final int KP_Delete = 0xFF9F;
  public static final int KP_Equal = 0xFFBD;
  public static final int KP_0 = 0xFFB0;
  public static final int KP_1 = 0xFFB1;
  public static final int KP_2 = 0xFFB2;
  public static final int KP_3 = 0xFFB3;
  public static final int KP_4 = 0xFFB4;
  public static final int KP_5 = 0xFFB5;
  public static final int KP_6 = 0xFFB6;
  public static final int KP_7 = 0xFFB7;
  public static final int KP_8 = 0xFFB8;
  public static final int KP_9 = 0xFFB9;
  public static final int KP_Decimal = 0xFFAE;
  public static final int KP_Add = 0xFFAB;
  public static final int KP_Subtract = 0xFFAD;
  public static final int KP_Multiply = 0xFFAA;
  public static final int KP_Divide = 0xFFAF;

  /*
   * Auxilliary Functions; note the duplicate definitions for left and right
   * function keys;  Sun keyboards and a few other manufactures have such
   * function key groups on the left and/or right sides of the keyboard.
   * We've not found a keyboard with more than 35 function keys total.
   */
  public static final int F1 = 0xFFBE;
  public static final int F2 = 0xFFBF;
  public static final int F3 = 0xFFC0;
  public static final int F4 = 0xFFC1;
  public static final int F5 = 0xFFC2;
  public static final int F6 = 0xFFC3;
  public static final int F7 = 0xFFC4;
  public static final int F8 = 0xFFC5;
  public static final int F9 = 0xFFC6;
  public static final int F10 = 0xFFC7;
  public static final int F11 = 0xFFC8;
  public static final int F12 = 0xFFC9;
  public static final int F13 = 0xFFCA;
  public static final int F14 = 0xFFCB;
  public static final int F15 = 0xFFCC;
  public static final int F16 = 0xFFCD;
  public static final int F17 = 0xFFCE;
  public static final int F18 = 0xFFCF;
  public static final int F19 = 0xFFD0;
  public static final int F20 = 0xFFD1;
  public static final int F21 = 0xFFD2;
  public static final int F22 = 0xFFD3;
  public static final int F23 = 0xFFD4;
  public static final int F24 = 0xFFD5;

  /* Modifiers */
  public static final int Shift_L = 0xFFE1;
  public static final int Shift_R = 0xFFE2;
  public static final int Control_L = 0xFFE3;
  public static final int Control_R = 0xFFE4;
  public static final int Caps_Lock = 0xFFE5;
  public static final int Shift_Lock = 0xFFE6;

  public static final int Meta_L = 0xFFE7;
  public static final int Meta_R = 0xFFE8;
  public static final int Alt_L = 0xFFE9;
  public static final int Alt_R = 0xFFEA;
  public static final int Super_L = 0xFFEB;
  public static final int Super_R = 0xFFEC;
  public static final int Hyper_L = 0xFFED;
  public static final int Hyper_R = 0xFFEE;

  /*
   * ISO 9995 Function and Modifier Keys
   * Byte 3 = 0xFE
   */
  public static final int ISO_Level3_Shift = 0xFE03;

  /*
   * Dead Modifier Keys
   */
  public static final int Dead_Grave = 0xfe50; /*                               COMBINING GRAVE ACCENT */
  public static final int Dead_Acute = 0xfe51; /*                               COMBINING ACUTE ACCENT */
  public static final int Dead_Circumflex = 0xfe52; /*                               COMBINING CIRCUMFLEX ACCENT */
  public static final int Dead_Tilde = 0xfe53; /*                               COMBINING TILDE */
  public static final int Dead_Macron = 0xfe54; /*                               COMBINING MACRON */
  public static final int Dead_Breve = 0xfe55; /*                               COMBINING BREVE */
  public static final int Dead_AboveDot = 0xfe56; /*                               COMBINING DOT ABOVE */
  public static final int Dead_Diaeresis = 0xfe57; /*                               COMBINING DIAERESIS */
  public static final int Dead_AboveRing = 0xfe58; /*                               COMBINING RING ABOVE */
  public static final int Dead_DoubleAcute = 0xfe59; /*                               COMBINING DOUBLE ACUTE ACCENT */
  public static final int Dead_Caron = 0xfe5a; /*                               COMBINING CARON */
  public static final int Dead_Cedilla = 0xfe5b; /*                               COMBINING CEDILLA */
  public static final int Dead_Ogonek = 0xfe5c; /*                               COMBINING OGONEK */
  public static final int Dead_Iota = 0xfe5d; /*                               MODIFIER LETTER SMALL IOTA */
  public static final int Dead_Voiced_Sound = 0xfe5e; /*                               COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK */
  public static final int Dead_SemiVoiced_Sound = 0xfe5f; /*                               COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */
  public static final int Dead_BelowDot = 0xfe60; /*                               COMBINING DOT BELOW */

  private static class KeySymbol {
    public KeySymbol(int keycode_, int[] keysym_) {
      keycode = keycode_;
      keysym = new int[5];
      System.arraycopy(keysym_, 0, keysym, 0, 5);
    }
    int keycode;
    int[] keysym;
  }

  private static KeySymbol[] keySymbols = {
    /*                 KEYCODE                                                    LOCATION                          */
    /*                                                UNKNOWN     STANDARD          LEFT        RIGHT       NUMPAD  */
    new KeySymbol(KeyEvent.VK_BACK_SPACE,   new int[]{VoidSymbol, BackSpace,        VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_TAB,          new int[]{VoidSymbol, Tab,              VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_ENTER,        new int[]{VoidSymbol, Return,           VoidSymbol, VoidSymbol, KP_Enter}),
    new KeySymbol(KeyEvent.VK_ESCAPE,       new int[]{VoidSymbol, Escape,           VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_CONTROL,      new int[]{VoidSymbol, Control_L,        Control_L,  Control_R,  VoidSymbol}),
    new KeySymbol(KeyEvent.VK_ALT_GRAPH,    new int[]{VoidSymbol, ISO_Level3_Shift, VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_ALT,          new int[]{VoidSymbol, ISO_Level3_Shift, Alt_L,      Alt_R,      VoidSymbol}),
    new KeySymbol(KeyEvent.VK_SHIFT,        new int[]{VoidSymbol, Shift_L,          Shift_L,    Shift_R,    VoidSymbol}),
    new KeySymbol(KeyEvent.VK_META,         new int[]{VoidSymbol, Meta_L,           Meta_L,     Meta_R,     VoidSymbol}),
    new KeySymbol(KeyEvent.VK_NUMPAD0,      new int[]{VoidSymbol, VoidSymbol,       VoidSymbol, VoidSymbol, KP_0}),
    new KeySymbol(KeyEvent.VK_NUMPAD1,      new int[]{VoidSymbol, VoidSymbol,       VoidSymbol, VoidSymbol, KP_1}),
    new KeySymbol(KeyEvent.VK_NUMPAD2,      new int[]{VoidSymbol, VoidSymbol,       VoidSymbol, VoidSymbol, KP_2}),
    new KeySymbol(KeyEvent.VK_NUMPAD3,      new int[]{VoidSymbol, VoidSymbol,       VoidSymbol, VoidSymbol, KP_3}),
    new KeySymbol(KeyEvent.VK_NUMPAD4,      new int[]{VoidSymbol, VoidSymbol,       VoidSymbol, VoidSymbol, KP_4}),
    new KeySymbol(KeyEvent.VK_NUMPAD5,      new int[]{VoidSymbol, VoidSymbol,       VoidSymbol, VoidSymbol, KP_5}),
    new KeySymbol(KeyEvent.VK_NUMPAD6,      new int[]{VoidSymbol, VoidSymbol,       VoidSymbol, VoidSymbol, KP_6}),
    new KeySymbol(KeyEvent.VK_NUMPAD7,      new int[]{VoidSymbol, VoidSymbol,       VoidSymbol, VoidSymbol, KP_7}),
    new KeySymbol(KeyEvent.VK_NUMPAD8,      new int[]{VoidSymbol, VoidSymbol,       VoidSymbol, VoidSymbol, KP_8}),
    new KeySymbol(KeyEvent.VK_NUMPAD9,      new int[]{VoidSymbol, VoidSymbol,       VoidSymbol, VoidSymbol, KP_9}),
    new KeySymbol(KeyEvent.VK_DECIMAL,      new int[]{VoidSymbol, KP_Decimal,       VoidSymbol, VoidSymbol, KP_Decimal}),
    new KeySymbol(KeyEvent.VK_ADD,          new int[]{VoidSymbol, KP_Add,           VoidSymbol, VoidSymbol, KP_Add}),
    new KeySymbol(KeyEvent.VK_SUBTRACT,     new int[]{VoidSymbol, KP_Subtract,      VoidSymbol, VoidSymbol, KP_Subtract}),
    new KeySymbol(KeyEvent.VK_MULTIPLY,     new int[]{VoidSymbol, KP_Multiply,      VoidSymbol, VoidSymbol, KP_Multiply}),
    new KeySymbol(KeyEvent.VK_DIVIDE,       new int[]{VoidSymbol, KP_Divide,        VoidSymbol, VoidSymbol, KP_Divide}),
    new KeySymbol(KeyEvent.VK_DELETE,       new int[]{VoidSymbol, Delete,           VoidSymbol, VoidSymbol, KP_Delete}),
    new KeySymbol(KeyEvent.VK_CLEAR,        new int[]{VoidSymbol, Clear,            VoidSymbol, VoidSymbol, KP_Begin}),
    new KeySymbol(KeyEvent.VK_HOME,         new int[]{VoidSymbol, Home,             VoidSymbol, VoidSymbol, KP_Home}),
    new KeySymbol(KeyEvent.VK_END,          new int[]{VoidSymbol, End,              VoidSymbol, VoidSymbol, KP_End}),
    new KeySymbol(KeyEvent.VK_PAGE_UP,      new int[]{VoidSymbol, Page_Up,          VoidSymbol, VoidSymbol, KP_Page_Up}),
    new KeySymbol(KeyEvent.VK_PAGE_DOWN,    new int[]{VoidSymbol, Page_Down,        VoidSymbol, VoidSymbol, KP_Page_Down}),
    new KeySymbol(KeyEvent.VK_UP,           new int[]{VoidSymbol, Up,               VoidSymbol, VoidSymbol, KP_Up}),
    new KeySymbol(KeyEvent.VK_DOWN,         new int[]{VoidSymbol, Down,             VoidSymbol, VoidSymbol, KP_Down}),
    new KeySymbol(KeyEvent.VK_LEFT,         new int[]{VoidSymbol, Left,             VoidSymbol, VoidSymbol, KP_Left}),
    new KeySymbol(KeyEvent.VK_RIGHT,        new int[]{VoidSymbol, Right,            VoidSymbol, VoidSymbol, KP_Right}),
    new KeySymbol(KeyEvent.VK_BEGIN,        new int[]{VoidSymbol, Begin,            VoidSymbol, VoidSymbol, KP_Begin}),
    new KeySymbol(KeyEvent.VK_KP_LEFT,      new int[]{VoidSymbol, KP_Left,          VoidSymbol, VoidSymbol, KP_Left}),
    new KeySymbol(KeyEvent.VK_KP_UP,        new int[]{VoidSymbol, KP_Up,            VoidSymbol, VoidSymbol, KP_Up}),
    new KeySymbol(KeyEvent.VK_KP_RIGHT,     new int[]{VoidSymbol, KP_Right,         VoidSymbol, VoidSymbol, KP_Right}),
    new KeySymbol(KeyEvent.VK_KP_DOWN,      new int[]{VoidSymbol, KP_Down,          VoidSymbol, VoidSymbol, KP_Down}),
    new KeySymbol(KeyEvent.VK_PRINTSCREEN,  new int[]{VoidSymbol, Print,            VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_SCROLL_LOCK,  new int[]{VoidSymbol, Scroll_Lock,      VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_CAPS_LOCK,    new int[]{VoidSymbol, Caps_Lock,        VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_NUM_LOCK,     new int[]{VoidSymbol, Num_Lock,         VoidSymbol, VoidSymbol, Num_Lock}),
    new KeySymbol(KeyEvent.VK_INSERT,       new int[]{VoidSymbol, Insert,           VoidSymbol, VoidSymbol, KP_Insert}),
    new KeySymbol(KeyEvent.VK_AGAIN,        new int[]{VoidSymbol, Redo,             VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_UNDO,         new int[]{VoidSymbol, Undo,             VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_FIND,         new int[]{VoidSymbol, Find,             VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_STOP,         new int[]{VoidSymbol, Cancel,           VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_HELP,         new int[]{VoidSymbol, Help,             VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_WINDOWS,      new int[]{VoidSymbol, Super_L,          Super_L,    Super_R,    VoidSymbol}),
    new KeySymbol(KeyEvent.VK_CONTEXT_MENU, new int[]{VoidSymbol, Menu,             VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_KANJI,        new int[]{VoidSymbol, Kanji,            VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_KATAKANA,     new int[]{VoidSymbol, Katakana,         VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_HIRAGANA,     new int[]{VoidSymbol, Hiragana,         VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_PREVIOUS_CANDIDATE, 
                                            new int[]{VoidSymbol, PreviousCandidate,VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_CODE_INPUT,   new int[]{VoidSymbol, Codeinput,        VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_JAPANESE_ROMAN, 
                                            new int[]{VoidSymbol, Romaji,           VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_KANA_LOCK,    new int[]{VoidSymbol, Kana_Lock,        VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_DEAD_ABOVEDOT,new int[]{VoidSymbol, Dead_AboveDot,    VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_DEAD_ABOVERING,
                                            new int[]{VoidSymbol, Dead_AboveRing,   VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_DEAD_ACUTE,   new int[]{VoidSymbol, Dead_Acute,       VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_DEAD_BREVE,   new int[]{VoidSymbol, Dead_Breve,       VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_DEAD_CARON,   new int[]{VoidSymbol, Dead_Caron,       VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_DEAD_CIRCUMFLEX,
                                            new int[]{VoidSymbol, Dead_Circumflex,  VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_DEAD_DIAERESIS,
                                            new int[]{VoidSymbol, Dead_Diaeresis,   VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_DEAD_DOUBLEACUTE,
                                            new int[]{VoidSymbol, Dead_DoubleAcute, VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_DEAD_GRAVE,   new int[]{VoidSymbol, Dead_Grave,       VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_DEAD_IOTA,    new int[]{VoidSymbol, Dead_Iota,        VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_DEAD_MACRON,  new int[]{VoidSymbol, Dead_Macron,      VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_DEAD_OGONEK,  new int[]{VoidSymbol, Dead_Ogonek,      VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_DEAD_SEMIVOICED_SOUND,
                                            new int[]{VoidSymbol, Dead_SemiVoiced_Sound,VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_DEAD_TILDE,   new int[]{VoidSymbol, Dead_Tilde,       VoidSymbol, VoidSymbol, VoidSymbol}),
    new KeySymbol(KeyEvent.VK_DEAD_VOICED_SOUND,
                                            new int[]{VoidSymbol, Dead_Voiced_Sound, VoidSymbol, VoidSymbol, VoidSymbol}),
  };

  public static int translateKeyEvent(KeyEvent ev) {
    int location = ev.getKeyLocation();
    int keyCode = ev.getKeyCode();

    // First check for function keys
    if (keyCode >= KeyEvent.VK_F1 && keyCode <= KeyEvent.VK_F12)
      return Keysyms.F1 + keyCode - KeyEvent.VK_F1;
    if (keyCode >= KeyEvent.VK_F13 && keyCode <= KeyEvent.VK_F24)
      return Keysyms.F13 + keyCode - KeyEvent.VK_F13;

    // Numpad numbers
    if (keyCode >= KeyEvent.VK_0 && keyCode <= KeyEvent.VK_9 &&
        location == KeyEvent.KEY_LOCATION_NUMPAD)
      return Keysyms.KP_0 + keyCode - KeyEvent.VK_0;

    if (VncViewer.os.startsWith("mac os x")) {
      // Alt on OS X behaves more like AltGr on other systems, and to get
      // sane behaviour we should translate things in that manner for the
      // remote VNC server. However that means we lose the ability to use
      // Alt as a shortcut modifier. Do what RealVNC does and hijack the
      // left command key as an Alt replacement.
      switch (keyCode) {
      case KeyEvent.VK_META:
        if (location == KeyEvent.KEY_LOCATION_LEFT)
          return Alt_L;
        else
          return Super_L;
      case KeyEvent.VK_ALT:
        if (location == KeyEvent.KEY_LOCATION_LEFT)
          return Alt_L;
        else
          return ISO_Level3_Shift;
      }
    }

    // Then other special keys
    if (keyCode == KeyEvent.VK_PAUSE)
      return (ev.isControlDown() ? Break : Pause);
    else if (keyCode == KeyEvent.VK_PRINTSCREEN)
      return (ev.isControlDown() ? Sys_Req : Print);
    else
      for(int i = 0; i < keySymbols.length; i++)
        if (keySymbols[i].keycode == keyCode)
          return (keySymbols[i].keysym)[location];

    // Unknown special key?
    if (KeyEvent.getKeyText(keyCode).isEmpty()) {
      vlog.error("Unknown key code:");
      String fmt = ev.paramString().replaceAll("%","%%");
      vlog.error(String.format(fmt.replaceAll(",","%n       ")));
      return VoidSymbol;
    }

    char keyChar = ev.getKeyChar();
    if (!ev.isControlDown() && keyChar != KeyEvent.CHAR_UNDEFINED)
      return UnicodeToKeysym.ucs2keysym(Character.toString(keyChar).codePointAt(0));

    int key = keyChar;
    if (ev.isControlDown()) {
      // For CTRL-<letter>, CTRL is sent separately, so just send <letter>.
      if ((key >= 1 && key <= 26 && !ev.isShiftDown()) ||
          // CTRL-{, CTRL-|, CTRL-} also map to ASCII 96-127
          (key >= 27 && key <= 29 && ev.isShiftDown()))
        key += 96;
      // For CTRL-SHIFT-<letter>, send capital <letter> to emulate behavior
      // of Linux.  For CTRL-@, send @.  For CTRL-_, send _.  For CTRL-^,
      // send ^.
      else if (key < 32)
        key += 64;
      // Windows and Mac sometimes return CHAR_UNDEFINED with CTRL-SHIFT
      // combinations, so best we can do is send the key code if it is
      // a valid ASCII symbol.
      else if (ev.getKeyChar() == KeyEvent.CHAR_UNDEFINED && keyCode >= 0 &&
               keyCode <= 127)
        key = keyCode;
    }
    return UnicodeToKeysym.ucs2keysym(key);
  }

  static LogWriter vlog = new LogWriter("Keysyms");
}