More fixes for key event issues.  It seems that on WindowsXP sending the actual key press/release separately can be mis-interpreted by the server as holding the key down.  Using a keyTyped event for all character input ensures that we can send an immediate down/up.  It also works around the broken AltGr detection in Java 6.  Also added support for more keys (F13-F24, etc.).  Some key definitions in java don't have X11 equivalents or I could not be sure so I've left them commented out.

git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@5066 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/java/com/tigervnc/rfb/Keysyms.java b/java/com/tigervnc/rfb/Keysyms.java
index 93aea12..4cf1784 100644
--- a/java/com/tigervnc/rfb/Keysyms.java
+++ b/java/com/tigervnc/rfb/Keysyms.java
@@ -1,5 +1,6 @@
 /* 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
@@ -27,6 +28,7 @@
 
 public class Keysyms {
 
+  public static final int ISO_Level3_Shift = 0xFE03;
   public static final int BackSpace = 0xFF08;
   public static final int Tab = 0xFF09;
   public static final int Linefeed = 0xFF0A;
@@ -38,6 +40,33 @@
   public static final int Escape = 0xFF1B;
   public static final int Delete = 0xFFFF;
 
+  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;
+
+  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 */
+
   public static final int Home = 0xFF50;
   public static final int Left = 0xFF51;
   public static final int Up = 0xFF52;
@@ -78,6 +107,17 @@
   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;
 
   public static final int Shift_L = 0xFFE1;
   public static final int Shift_R = 0xFFE2;
diff --git a/java/com/tigervnc/vncviewer/CConn.java b/java/com/tigervnc/vncviewer/CConn.java
index 09fa2d7..97ba9da 100644
--- a/java/com/tigervnc/vncviewer/CConn.java
+++ b/java/com/tigervnc/vncviewer/CConn.java
@@ -67,6 +67,9 @@
     new PixelFormat(8, 6, false, true, 3, 3, 3, 4, 2, 0);
   static final PixelFormat mediumColourPF = 
     new PixelFormat(8, 8, false, false, 7, 7, 3, 0, 3, 6);
+  static final int KEY_LOC_SHIFT_R = 0;
+  static final int KEY_LOC_SHIFT_L = 16;
+  static final int SUPER_MASK = 1<<15;
 
   ////////////////////////////////////////////////////////////////////
   // The following methods are all called from the RFB thread
@@ -1154,8 +1157,31 @@
     writer().writeKeyEvent(keysym, down);
   }
 
+  public void writeKeyEvent(KeyEvent ev, int keysym) {
+    if (keysym < 0)
+      return;
+    String fmt = ev.paramString().replaceAll("%","%%");
+    vlog.debug(String.format(fmt.replaceAll(",","%n       ")));
+    // Windows sends an extra CTRL_L + ALT_R when AltGr is down that need to
+    // be suppressed for keyTyped events. In Java 6 KeyEvent.isAltGraphDown()
+    // is broken for keyPressed/keyReleased events.
+    int ALTGR_MASK = ((Event.CTRL_MASK<<KEY_LOC_SHIFT_L) | Event.ALT_MASK);
+    String os = System.getProperty("os.name");
+    if (os.startsWith("Windows") && ((modifiers & ALTGR_MASK) != 0)) {
+      writeKeyEvent(Keysyms.Control_L, false);
+      writeKeyEvent(Keysyms.Alt_R, false);
+      writeKeyEvent(keysym, true);
+      writeKeyEvent(keysym, false);
+      writeKeyEvent(Keysyms.Control_L, true);
+      writeKeyEvent(Keysyms.Alt_R, true);
+    } else {
+      writeKeyEvent(keysym, true);
+      writeKeyEvent(keysym, false);
+    }
+  }
+
   public void writeKeyEvent(KeyEvent ev) {
-    int keysym = 0, keycode, key, location;
+    int keysym = 0, keycode, key, location, locationShift;
 
     if (shuttingDown)
       return;
@@ -1163,11 +1189,14 @@
     boolean down = (ev.getID() == KeyEvent.KEY_PRESSED);
 
     keycode = ev.getKeyCode();
+    if (keycode == KeyEvent.VK_UNDEFINED)
+      return;
     key = ev.getKeyChar();
     location = ev.getKeyLocation();
-
-    String fmt = ev.paramString().replaceAll("%","%%");
-    vlog.debug(String.format(fmt.replaceAll(",","%n       ")));
+    if (location == KeyEvent.KEY_LOCATION_RIGHT)
+      locationShift = KEY_LOC_SHIFT_R;
+    else
+      locationShift = KEY_LOC_SHIFT_L;
 
     if (!ev.isActionKey()) {
       if (keycode >= KeyEvent.VK_0 && keycode <= KeyEvent.VK_9 &&
@@ -1209,43 +1238,37 @@
         else
           keysym = Keysyms.Clear;  break;
       case KeyEvent.VK_CONTROL:
-        // Suppress CTRL+ALT when AltGr is pressed
-        if (ev.isAltGraphDown())
-          return;
         if (down)
-          modifiers |= Event.CTRL_MASK;
+          modifiers |= (Event.CTRL_MASK<<locationShift);
         else
-          modifiers &= ~Event.CTRL_MASK;
+          modifiers &= ~(Event.CTRL_MASK<<locationShift);
         if (location == KeyEvent.KEY_LOCATION_RIGHT)
           keysym = Keysyms.Control_R;
         else
           keysym = Keysyms.Control_L;  break;
       case KeyEvent.VK_ALT:
-        // Suppress CTRL+ALT when AltGr is pressed
-        if (ev.isAltGraphDown())
-          return;
         if (down)
-          modifiers |= Event.ALT_MASK;
+          modifiers |= (Event.ALT_MASK<<locationShift);
         else
-          modifiers &= ~Event.ALT_MASK;
+          modifiers &= ~(Event.ALT_MASK<<locationShift);
         if (location == KeyEvent.KEY_LOCATION_RIGHT)
           keysym = Keysyms.Alt_R;
         else
           keysym = Keysyms.Alt_L;  break;
       case KeyEvent.VK_SHIFT:
         if (down)
-          modifiers |= Event.SHIFT_MASK;
+          modifiers |= (Event.SHIFT_MASK<<locationShift);
         else
-          modifiers &= ~Event.SHIFT_MASK;
+          modifiers &= ~(Event.SHIFT_MASK<<locationShift);
         if (location == KeyEvent.KEY_LOCATION_RIGHT)
           keysym = Keysyms.Shift_R;
         else
           keysym = Keysyms.Shift_L;  break;
       case KeyEvent.VK_META:
         if (down)
-          modifiers |= Event.META_MASK;
+          modifiers |= (Event.META_MASK<<locationShift);
         else
-          modifiers &= ~Event.META_MASK;
+          modifiers &= ~(Event.META_MASK<<locationShift);
         if (location == KeyEvent.KEY_LOCATION_RIGHT)
           keysym = Keysyms.Meta_R;
         else
@@ -1269,6 +1292,7 @@
                    keycode <= 127)
             key = keycode;
         }
+
         keysym = UnicodeToKeysym.translate(key);
         if (keysym == -1)
           return;
@@ -1316,6 +1340,15 @@
           keysym = Keysyms.KP_Right;
         else
           keysym = Keysyms.Right;  break;
+      case KeyEvent.VK_BEGIN:
+        if (location == KeyEvent.KEY_LOCATION_NUMPAD)
+          keysym = Keysyms.KP_Begin;
+        else
+          keysym = Keysyms.Begin;  break;
+      case KeyEvent.VK_KP_LEFT:      keysym = Keysyms.KP_Left; break;
+      case KeyEvent.VK_KP_UP:        keysym = Keysyms.KP_Up; break;
+      case KeyEvent.VK_KP_RIGHT:     keysym = Keysyms.KP_Right; break;
+      case KeyEvent.VK_KP_DOWN:      keysym = Keysyms.KP_Down; break;
       case KeyEvent.VK_F1:           keysym = Keysyms.F1; break;
       case KeyEvent.VK_F2:           keysym = Keysyms.F2; break;
       case KeyEvent.VK_F3:           keysym = Keysyms.F3; break;
@@ -1329,7 +1362,21 @@
       case KeyEvent.VK_F11:          keysym = Keysyms.F11; break;
       case KeyEvent.VK_F12:          keysym = Keysyms.F12; break;
       case KeyEvent.VK_F13:          keysym = Keysyms.F13; break;
+      case KeyEvent.VK_F14:          keysym = Keysyms.F14; break;
+      case KeyEvent.VK_F15:          keysym = Keysyms.F15; break;
+      case KeyEvent.VK_F16:          keysym = Keysyms.F16; break;
+      case KeyEvent.VK_F17:          keysym = Keysyms.F17; break;
+      case KeyEvent.VK_F18:          keysym = Keysyms.F18; break;
+      case KeyEvent.VK_F19:          keysym = Keysyms.F19; break;
+      case KeyEvent.VK_F20:          keysym = Keysyms.F20; break;
+      case KeyEvent.VK_F21:          keysym = Keysyms.F21; break;
+      case KeyEvent.VK_F22:          keysym = Keysyms.F22; break;
+      case KeyEvent.VK_F23:          keysym = Keysyms.F23; break;
+      case KeyEvent.VK_F24:          keysym = Keysyms.F24; break;
       case KeyEvent.VK_PRINTSCREEN:  keysym = Keysyms.Print; break;
+      case KeyEvent.VK_SCROLL_LOCK:  keysym = Keysyms.Scroll_Lock; break;
+      case KeyEvent.VK_CAPS_LOCK:    keysym = Keysyms.Caps_Lock; break;
+      case KeyEvent.VK_NUM_LOCK:     keysym = Keysyms.Num_Lock; break;
       case KeyEvent.VK_PAUSE:
         if (ev.isControlDown())
           keysym = Keysyms.Break;
@@ -1341,27 +1388,55 @@
           keysym = Keysyms.KP_Insert;
         else
           keysym = Keysyms.Insert;  break;
-      case KeyEvent.VK_KP_DOWN:      keysym = Keysyms.KP_Down; break;
-      case KeyEvent.VK_KP_LEFT:      keysym = Keysyms.KP_Left; break;
-      case KeyEvent.VK_KP_RIGHT:     keysym = Keysyms.KP_Right; break;
-      case KeyEvent.VK_KP_UP:        keysym = Keysyms.KP_Up; break;
-      case KeyEvent.VK_NUM_LOCK:     keysym = Keysyms.Num_Lock; break;
-      case KeyEvent.VK_WINDOWS:      keysym = Keysyms.Super_L; break;
-      case KeyEvent.VK_CONTEXT_MENU: keysym = Keysyms.Menu; break;
-      case KeyEvent.VK_SCROLL_LOCK:  keysym = Keysyms.Scroll_Lock; break;
-      case KeyEvent.VK_CAPS_LOCK:    keysym = Keysyms.Caps_Lock; break;
-      case KeyEvent.VK_BEGIN:
-        if (location == KeyEvent.KEY_LOCATION_NUMPAD)
-          keysym = Keysyms.KP_Begin;
+      // case KeyEvent.VK_FINAL:     keysym = Keysyms.?; break;
+      // case KeyEvent.VK_CONVERT:     keysym = Keysyms.?; break;
+      // case KeyEvent.VK_NONCONVERT:     keysym = Keysyms.?; break;
+      // case KeyEvent.VK_ACCEPT:     keysym = Keysyms.?; break;
+      // case KeyEvent.VK_MODECHANGE:     keysym = Keysyms.Mode_switch?; break;
+      // case KeyEvent.VK_KANA:     keysym = Keysyms.Kana_shift?; break;
+      case KeyEvent.VK_KANJI:     keysym = Keysyms.Kanji; break;
+      // case KeyEvent.VK_ALPHANUMERIC:     keysym = Keysyms.Eisu_Shift?; break;
+      case KeyEvent.VK_KATAKANA:     keysym = Keysyms.Katakana; break;
+      case KeyEvent.VK_HIRAGANA:     keysym = Keysyms.Hiragana; break;
+      // case KeyEvent.VK_FULL_WIDTH:     keysym = Keysyms.?; break;
+      // case KeyEvent.VK_HALF_WIDTH:     keysym = Keysyms.?; break;
+      // case KeyEvent.VK_ROMAN_CHARACTERS:     keysym = Keysyms.?; break;
+      // case KeyEvent.VK_ALL_CANDIDATES:     keysym = Keysyms.MultipleCandidate?; break;
+      case KeyEvent.VK_PREVIOUS_CANDIDATE:     keysym = Keysyms.PreviousCandidate; break;
+      case KeyEvent.VK_CODE_INPUT:     keysym = Keysyms.Codeinput; break;
+      // case KeyEvent.VK_JAPANESE_KATAKANA:     keysym = Keysyms.?; break;
+      // case KeyEvent.VK_JAPANESE_HIRAGANA:     keysym = Keysyms.?; break;
+      case KeyEvent.VK_JAPANESE_ROMAN:     keysym = Keysyms.Romaji; break;
+      case KeyEvent.VK_KANA_LOCK:     keysym = Keysyms.Kana_Lock; break;
+      // case KeyEvent.VK_INPUT_METHOD_ON_OFF:     keysym = Keysyms.?; break;
+
+      case KeyEvent.VK_AGAIN:     keysym = Keysyms.Redo; break;
+      case KeyEvent.VK_UNDO:     keysym = Keysyms.Undo; break;
+      // case KeyEvent.VK_COPY:     keysym = Keysyms.?; break;
+      // case KeyEvent.VK_PASTE:     keysym = Keysyms.?; break;
+      // case KeyEvent.VK_CUT:     keysym = Keysyms.?; break;
+      case KeyEvent.VK_FIND:     keysym = Keysyms.Find; break;
+      // case KeyEvent.VK_PROPS:     keysym = Keysyms.?; break;
+      case KeyEvent.VK_STOP:     keysym = Keysyms.Cancel; break;
+      case KeyEvent.VK_HELP:         keysym = Keysyms.Help; break;
+      case KeyEvent.VK_WINDOWS:
+        if (down)
+          modifiers |= SUPER_MASK;
         else
-          keysym = Keysyms.Begin;  break;
+          modifiers &= ~SUPER_MASK;
+        keysym = Keysyms.Super_L; break;
+      case KeyEvent.VK_CONTEXT_MENU: keysym = Keysyms.Menu; break;
       default: return;
       }
     }
 
-    writeKeyEvent(keysym, down);
-  }
+    if (keysym > 0) {
+      String fmt = ev.paramString().replaceAll("%","%%");
+      vlog.debug(String.format(fmt.replaceAll(",","%n       ")));
 
+      writeKeyEvent(keysym, down);
+    }
+  }
 
   public void writePointerEvent(MouseEvent ev) {
     if (state() != RFBSTATE_NORMAL || shuttingDown)
@@ -1390,7 +1465,6 @@
     writer().writePointerEvent(new Point(ev.getX(), ev.getY()), buttonMask);
   }
 
-
   public void writeWheelEvent(MouseWheelEvent ev) {
     if (state() != RFBSTATE_NORMAL || shuttingDown)
       return;
@@ -1411,16 +1485,25 @@
 
   }
 
-
   synchronized void releaseModifiers() {
-    if ((modifiers & Event.SHIFT_MASK) != 0)
+    if ((modifiers & Event.SHIFT_MASK) == Event.SHIFT_MASK)
+      writeKeyEvent(Keysyms.Shift_R, false);
+    if (((modifiers>>KEY_LOC_SHIFT_L) & Event.SHIFT_MASK) == Event.SHIFT_MASK)
       writeKeyEvent(Keysyms.Shift_L, false);
-    if ((modifiers & Event.CTRL_MASK) != 0)
+    if ((modifiers & Event.CTRL_MASK) == Event.CTRL_MASK)
+      writeKeyEvent(Keysyms.Control_R, false);
+    if (((modifiers>>KEY_LOC_SHIFT_L) & Event.CTRL_MASK) == Event.CTRL_MASK)
       writeKeyEvent(Keysyms.Control_L, false);
-    if ((modifiers & Event.ALT_MASK) != 0)
+    if ((modifiers & Event.ALT_MASK) == Event.ALT_MASK)
+      writeKeyEvent(Keysyms.Alt_R, false);
+    if (((modifiers>>KEY_LOC_SHIFT_L) & Event.ALT_MASK) == Event.ALT_MASK)
       writeKeyEvent(Keysyms.Alt_L, false);
-    if ((modifiers & Event.META_MASK) != 0)
+    if ((modifiers & Event.META_MASK) == Event.META_MASK)
+      writeKeyEvent(Keysyms.Meta_R, false);
+    if (((modifiers>>KEY_LOC_SHIFT_L) & Event.META_MASK) == Event.META_MASK)
       writeKeyEvent(Keysyms.Meta_L, false);
+    if ((modifiers & SUPER_MASK) == SUPER_MASK)
+      writeKeyEvent(Keysyms.Super_L, false);
     modifiers = 0;
   }
 
diff --git a/java/com/tigervnc/vncviewer/DesktopWindow.java b/java/com/tigervnc/vncviewer/DesktopWindow.java
index 5f9ee1a..7621af5 100644
--- a/java/com/tigervnc/vncviewer/DesktopWindow.java
+++ b/java/com/tigervnc/vncviewer/DesktopWindow.java
@@ -437,12 +437,19 @@
   }
 
   // Handle the key-typed event.
-  public void keyTyped(KeyEvent e) {}
+  public void keyTyped(KeyEvent e) {
+    int keysym = UnicodeToKeysym.translate(e.getKeyChar());
+    if (!cc.viewer.viewOnly.getValue())
+      if (!e.isActionKey() && keysym > 0)
+        cc.writeKeyEvent(e, keysym);
+  }
 
   // Handle the key-released event.
   public void keyReleased(KeyEvent e) {
+    int keysym = UnicodeToKeysym.translate(e.getKeyChar());
     if (!cc.viewer.viewOnly.getValue())
-      cc.writeKeyEvent(e);
+      if (e.isActionKey() || keysym < 0)
+        cc.writeKeyEvent(e);
   }
 
   // Handle the key-pressed event.
@@ -499,8 +506,10 @@
           return;
       }
     }
+    int keysym = UnicodeToKeysym.translate(e.getKeyChar());
     if (!cc.viewer.viewOnly.getValue())
-      cc.writeKeyEvent(e);
+      if (e.isActionKey() || keysym < 0)
+        cc.writeKeyEvent(e);
   }
 
   ////////////////////////////////////////////////////////////////////