]> source.dussan.org Git - tigervnc.git/commitdiff
Backported a number of fixes/enhancements (mostly related to keymapping) from TurboVN...
authorBrian Hinz <bphinz@users.sourceforge.net>
Sun, 3 Mar 2013 16:53:41 +0000 (16:53 +0000)
committerBrian Hinz <bphinz@users.sourceforge.net>
Sun, 3 Mar 2013 16:53:41 +0000 (16:53 +0000)
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@5056 3789f03b-4d11-0410-bbf8-ca57d06f2519

java/com/tigervnc/rfb/Keysyms.java
java/com/tigervnc/vncviewer/CConn.java
java/com/tigervnc/vncviewer/DesktopWindow.java

index 5e1bf954581c2e7aba3f89e85303420bbb928684..93aea127a5d20fda96163b50b45ad2fed126aa42 100644 (file)
@@ -1,15 +1,16 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * 
+ * Copyright (C) 2012-2013 D. R. Commander.  All Rights Reserved.
+ *
  * 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,
@@ -76,6 +77,7 @@ public class Keysyms {
   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 Shift_L = 0xFFE1;
   public static final int Shift_R = 0xFFE2;
@@ -85,4 +87,35 @@ public class Keysyms {
   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 Caps_Lock = 0xFFE5;
+
+  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_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;
 }
index ce858c9f3738ec59f47ce8ef4f90a117eb258796..0a7526eee8240aa165c55d187a3f9ba239a3f55d 100644 (file)
@@ -1,18 +1,18 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
  * Copyright 2009-2011 Pierre Ossman <ossman@cendio.se> for Cendio AB
- * Copyright (C) 2011 D. R. Commander.  All Rights Reserved.
+ * Copyright (C) 2011-2013 D. R. Commander.  All Rights Reserved.
  * Copyright (C) 2011-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,
@@ -71,7 +71,7 @@ public class CConn extends CConnection
   ////////////////////////////////////////////////////////////////////
   // The following methods are all called from the RFB thread
 
-  public CConn(VncViewer viewer_, Socket sock_, 
+  public CConn(VncViewer viewer_, Socket sock_,
                String vncServerName) 
   {
     serverHost = null; serverPort = 0; sock = sock_; viewer = viewer_; 
@@ -86,9 +86,8 @@ public class CConn extends CConnection
     options = new OptionsDialog(this);
     options.initDialog();
     clipboardDialog = new ClipboardDialog(this);
-    firstUpdate = true; pendingUpdate = false; continuousUpdates = false; 
+    firstUpdate = true; pendingUpdate = false; continuousUpdates = false;
     forceNonincremental = true; supportsSyncFence = false;
-    
 
     setShared(viewer.shared.getValue());
     upg = this;
@@ -113,7 +112,7 @@ public class CConn extends CConnection
 
     if (sock != null) {
       String name = sock.getPeerEndpoint();
-      vlog.info("Accepted connection from "+name);
+      vlog.info("Accepted connection from " + name);
     } else {
       if (vncServerName != null &&
           !viewer.alwaysShowServerDialog.getValue()) {
@@ -153,7 +152,6 @@ public class CConn extends CConnection
     if (supportsSyncFence)
       requestNewUpdate();
   }
-  
 
   public boolean showMsgBox(int flags, String title, String text)
   {
@@ -167,7 +165,7 @@ public class CConn extends CConnection
     if (viewport != null)
       viewport.dispose();
     viewport = null;
-  } 
+  }
 
   // blockCallback() is called when reading from the socket would block.
   public void blockCallback() {
@@ -178,7 +176,7 @@ public class CConn extends CConnection
     } catch (java.lang.InterruptedException e) {
       throw new Exception(e.getMessage());
     }
-  }  
+  }
 
   // getUserPasswd() is called by the CSecurity object when it needs us to read
   // a password from the user.
@@ -203,8 +201,7 @@ public class CConn extends CConnection
       } catch(IOException e) {
         throw new Exception("Failed to read VncPasswd file");
       }
-      String PlainPasswd =
-        VncAuth.unobfuscatePasswd(obfPwd);
+      String PlainPasswd = VncAuth.unobfuscatePasswd(obfPwd);
       passwd.append(PlainPasswd);
       passwd.setLength(PlainPasswd.length());
       return true;
@@ -270,7 +267,7 @@ public class CConn extends CConnection
   // setDesktopSize() is called when the desktop size changes (including when
   // it is set initially).
   public void setDesktopSize(int w, int h) {
-    super.setDesktopSize(w,h);
+    super.setDesktopSize(w, h);
     resizeFramebuffer();
   }
 
@@ -281,7 +278,7 @@ public class CConn extends CConnection
 
     if ((reason == screenTypes.reasonClient) &&
         (result != screenTypes.resultSuccess)) {
-      vlog.error("SetDesktopSize failed: "+result);
+      vlog.error("SetDesktopSize failed: " + result);
       return;
     }
 
@@ -289,7 +286,7 @@ public class CConn extends CConnection
   }
 
   // clientRedirect() migrates the client to another host/port
-  public void clientRedirect(int port, String host, 
+  public void clientRedirect(int port, String host,
                              String x509subject) {
     try {
       sock.close();
@@ -440,20 +437,20 @@ public class CConn extends CConnection
   {
     // can't call super.super.fence(flags, len, data);
     cp.supportsFence = true;
-  
+
     if ((flags & fenceTypes.fenceFlagRequest) != 0) {
       // We handle everything synchronously so we trivially honor these modes
       flags = flags & (fenceTypes.fenceFlagBlockBefore | fenceTypes.fenceFlagBlockAfter);
-  
+
       writer().writeFence(flags, len, data);
       return;
     }
-  
+
     if (len == 0) {
       // Initial probe
       if ((flags & fenceTypes.fenceFlagSyncNext) != 0) {
         supportsSyncFence = true;
-  
+
         if (cp.supportsContinuousUpdates) {
           vlog.info("Enabling continuous updates");
           continuousUpdates = true;
@@ -464,9 +461,9 @@ public class CConn extends CConnection
       // Pixel format change
       MemInStream memStream = new MemInStream(data, 0, len);
       PixelFormat pf = new PixelFormat();
-  
+
       pf.read(memStream);
-  
+
       desktop.setServerPF(pf);
       cp.setPF(pf);
     }
@@ -647,9 +644,9 @@ public class CConn extends CConnection
         // get the response back. That way we will be synchronised with
         // when the server switches.
         MemOutStream memStream = new MemOutStream();
-  
+
         pf.write(memStream);
-  
+
         writer().writeFence(fenceTypes.fenceFlagRequest | fenceTypes.fenceFlagSyncNext,
                             memStream.length(), (byte[])memStream.data());
       } else {
@@ -661,7 +658,7 @@ public class CConn extends CConnection
       }
 
       String str = pf.print();
-      vlog.info("Using pixel format "+str);
+      vlog.info("Using pixel format " + str);
       writer().writeSetPixelFormat(pf);
 
       formatChange = false;
@@ -671,7 +668,7 @@ public class CConn extends CConnection
 
     if (forceNonincremental || !continuousUpdates) {
       pendingUpdate = true;
-      writer().writeFramebufferUpdateRequest(new Rect(0,0,cp.width,cp.height),
+      writer().writeFramebufferUpdateRequest(new Rect(0, 0, cp.width, cp.height),
                                                  !forceNonincremental);
     }
 
@@ -1009,9 +1006,9 @@ public class CConn extends CConnection
       if (desktop != null)
         desktop.resetLocalCursor();
     }
-    
+
     checkEncodings();
-  
+
     if (state() != RFBSTATE_NORMAL) {
       /* Process security types which don't use encryption */
       if (options.encNone.isSelected()) {
@@ -1133,84 +1130,180 @@ public class CConn extends CConnection
 
   // writeClientCutText() is called from the clipboard dialog
   public void writeClientCutText(String str, int len) {
-    if (state() != RFBSTATE_NORMAL) return;
-    writer().writeClientCutText(str,len);
+    if (state() != RFBSTATE_NORMAL || shuttingDown)
+      return;
+    writer().writeClientCutText(str, len);
   }
 
   public void writeKeyEvent(int keysym, boolean down) {
-    if (state() != RFBSTATE_NORMAL) return;
+    if (state() != RFBSTATE_NORMAL || shuttingDown)
+      return;
     writer().writeKeyEvent(keysym, down);
   }
 
   public void writeKeyEvent(KeyEvent ev) {
-    if (ev.getID() != KeyEvent.KEY_PRESSED && !ev.isActionKey())
+    int keysym = 0, keycode, key, location, fakeModifiers = 0;
+
+    if (shuttingDown)
       return;
 
-    int keysym, keycode, currentModifiers;
+    boolean down = (ev.getID() == KeyEvent.KEY_PRESSED);
 
-    currentModifiers = ev.getModifiers();
     keycode = ev.getKeyCode();
+    key = ev.getKeyChar();
+    location = ev.getKeyLocation();
 
-    if (!ev.isActionKey()) {
-      vlog.debug("key press "+ev.getKeyChar());
-      if (ev.getKeyChar() < 32) {
-        // if the ctrl modifier key is down, send the equivalent ASCII since we
-        // will send the ctrl modifier anyway
-
-        if ((currentModifiers & KeyEvent.CTRL_MASK) != 0) {
-          if ((currentModifiers & KeyEvent.SHIFT_MASK) != 0) {
-            keysym = ev.getKeyChar() + 64;
-            if (keysym == -1)
-              return;
-          } else { 
-            keysym = ev.getKeyChar() + 96;
-            if (keysym == 127) keysym = 95;
-          }
-        } else {
-          switch (keycode) {
-          case KeyEvent.VK_BACK_SPACE: keysym = Keysyms.BackSpace; break;
-          case KeyEvent.VK_TAB:        keysym = Keysyms.Tab; break;
-          case KeyEvent.VK_ENTER:      keysym = Keysyms.Return; break;
-          case KeyEvent.VK_ESCAPE:     keysym = Keysyms.Escape; break;
-          default: return;
-          }
-        }
+    vlog.debug((ev.isActionKey() ? "action " : "") + "key " +
+               (down ? "PRESS" : "release") + " code " + keycode +
+                " location " + location + " ASCII " + key);
 
-      } else if (ev.getKeyChar() == 127) {
-        keysym = Keysyms.Delete;
+    if (!ev.isActionKey()) {
+      if (keycode >= KeyEvent.VK_0 && keycode <= KeyEvent.VK_9 &&
+        location == KeyEvent.KEY_LOCATION_NUMPAD)
+        keysym = Keysyms.KP_0 + keycode - KeyEvent.VK_0;
 
-      } else {
-        keysym = UnicodeToKeysym.translate(ev.getKeyChar());
+      switch (keycode) {
+      case KeyEvent.VK_BACK_SPACE: keysym = Keysyms.BackSpace; break;
+      case KeyEvent.VK_TAB:        keysym = Keysyms.Tab; break;
+      case KeyEvent.VK_ENTER:
+        if (location == KeyEvent.KEY_LOCATION_NUMPAD)
+          keysym = Keysyms.KP_Enter;
+        else
+          keysym = Keysyms.Return;  break;
+      case KeyEvent.VK_ESCAPE:     keysym = Keysyms.Escape; break;
+      case KeyEvent.VK_NUMPAD0:    keysym = Keysyms.KP_0; break;
+      case KeyEvent.VK_NUMPAD1:    keysym = Keysyms.KP_1; break;
+      case KeyEvent.VK_NUMPAD2:    keysym = Keysyms.KP_2; break;
+      case KeyEvent.VK_NUMPAD3:    keysym = Keysyms.KP_3; break;
+      case KeyEvent.VK_NUMPAD4:    keysym = Keysyms.KP_4; break;
+      case KeyEvent.VK_NUMPAD5:    keysym = Keysyms.KP_5; break;
+      case KeyEvent.VK_NUMPAD6:    keysym = Keysyms.KP_6; break;
+      case KeyEvent.VK_NUMPAD7:    keysym = Keysyms.KP_7; break;
+      case KeyEvent.VK_NUMPAD8:    keysym = Keysyms.KP_8; break;
+      case KeyEvent.VK_NUMPAD9:    keysym = Keysyms.KP_9; break;
+      case KeyEvent.VK_DECIMAL:    keysym = Keysyms.KP_Decimal; break;
+      case KeyEvent.VK_ADD:        keysym = Keysyms.KP_Add; break;
+      case KeyEvent.VK_SUBTRACT:   keysym = Keysyms.KP_Subtract; break;
+      case KeyEvent.VK_MULTIPLY:   keysym = Keysyms.KP_Multiply; break;
+      case KeyEvent.VK_DIVIDE:     keysym = Keysyms.KP_Divide; break;
+      case KeyEvent.VK_DELETE:
+        if (location == KeyEvent.KEY_LOCATION_NUMPAD)
+          keysym = Keysyms.KP_Delete;
+        else
+          keysym = Keysyms.Delete;  break;
+      case KeyEvent.VK_CLEAR:
+        if (location == KeyEvent.KEY_LOCATION_NUMPAD)
+          keysym = Keysyms.KP_Begin;
+        else
+          keysym = Keysyms.Clear;  break;
+      case KeyEvent.VK_CONTROL:
+        if (down)
+          modifiers |= Event.CTRL_MASK;
+        else
+          modifiers &= ~Event.CTRL_MASK;
+        if (location == KeyEvent.KEY_LOCATION_RIGHT)
+          keysym = Keysyms.Control_R;
+        else
+          keysym = Keysyms.Control_L;  break;
+      case KeyEvent.VK_ALT:
+        if (down)
+          modifiers |= Event.ALT_MASK;
+        else
+          modifiers &= ~Event.ALT_MASK;
+        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;
+        else
+          modifiers &= ~Event.SHIFT_MASK;
+        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;
+        else
+          modifiers &= ~Event.META_MASK;
+        if (location == KeyEvent.KEY_LOCATION_RIGHT)
+          keysym = Keysyms.Meta_R;
+        else
+          keysym = Keysyms.Meta_L;  break;
+      default:
+        if (ev.isControlDown() && ev.isAltDown()) {
+          // Handle AltGr key on international keyboards
+          if ((keycode >= 32 && keycode <= 126) ||
+              (keycode >= 160 && keycode <= 255))
+            key = keycode;
+            fakeModifiers |= Event.ALT_MASK | Event.CTRL_MASK;
+        } else 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 (key == KeyEvent.CHAR_UNDEFINED && keycode >= 0 &&
+                   keycode <= 127)
+            key = keycode;
+        }
+        keysym = UnicodeToKeysym.translate(key);
         if (keysym == -1)
           return;
-
-        // Windows 7 or some Java version send key events that require the
-        // following special treatment with the German Alt-Gr Key. They send
-        // ALT + CTRL before the normal key event. They should be suppressed
-        if ((currentModifiers & KeyEvent.CTRL_MASK) != 0
-               && (currentModifiers & KeyEvent.ALT_MASK) != 0
-               && ((keysym == 0x5c) || (keysym == 0x7c)        // backslash bar
-                || (keysym == 0x5b) || (keysym == 0x5d)        // [ ]
-                || (keysym == 0x7b) || (keysym == 0x7d)        // { }
-                || (keysym == 0x7e) || (keysym == 0x40)        // ~ @
-                || (keysym == 0x20ac) || (keysym == 0xb5)      // Euro Micro
-                || (keysym == 0xb2) || (keysym == 0xb3))       // ^2 ^3
-                  )
-          currentModifiers &= (~ KeyEvent.CTRL_MASK) & (~ KeyEvent.ALT_MASK);
       }
-
     } else {
       // KEY_ACTION
-      vlog.debug("key action " + keycode);
       switch (keycode) {
-      case KeyEvent.VK_HOME:         keysym = Keysyms.Home; break;
-      case KeyEvent.VK_END:          keysym = Keysyms.End; break;
-      case KeyEvent.VK_PAGE_UP:      keysym = Keysyms.Page_Up; break;
-      case KeyEvent.VK_PAGE_DOWN:    keysym = Keysyms.Page_Down; break;
-      case KeyEvent.VK_UP:           keysym = Keysyms.Up; break;
-      case KeyEvent.VK_DOWN:         keysym = Keysyms.Down; break;
-      case KeyEvent.VK_LEFT:         keysym = Keysyms.Left; break;
-      case KeyEvent.VK_RIGHT:        keysym = Keysyms.Right; break;
+      case KeyEvent.VK_HOME:
+        if (location == KeyEvent.KEY_LOCATION_NUMPAD)
+          keysym = Keysyms.KP_Home;
+        else
+          keysym = Keysyms.Home;  break;
+      case KeyEvent.VK_END:
+        if (location == KeyEvent.KEY_LOCATION_NUMPAD)
+          keysym = Keysyms.KP_End;
+        else
+          keysym = Keysyms.End;  break;
+      case KeyEvent.VK_PAGE_UP:
+        if (location == KeyEvent.KEY_LOCATION_NUMPAD)
+          keysym = Keysyms.KP_Page_Up;
+        else
+          keysym = Keysyms.Page_Up;  break;
+      case KeyEvent.VK_PAGE_DOWN:
+        if (location == KeyEvent.KEY_LOCATION_NUMPAD)
+          keysym = Keysyms.KP_Page_Down;
+        else
+          keysym = Keysyms.Page_Down;  break;
+      case KeyEvent.VK_UP:
+        if (location == KeyEvent.KEY_LOCATION_NUMPAD)
+          keysym = Keysyms.KP_Up;
+        else
+          keysym = Keysyms.Up;  break;
+      case KeyEvent.VK_DOWN:
+        if (location == KeyEvent.KEY_LOCATION_NUMPAD)
+          keysym = Keysyms.KP_Down;
+        else
+         keysym = Keysyms.Down;  break;
+      case KeyEvent.VK_LEFT:
+        if (location == KeyEvent.KEY_LOCATION_NUMPAD)
+          keysym = Keysyms.KP_Left;
+        else
+         keysym = Keysyms.Left;  break;
+      case KeyEvent.VK_RIGHT:
+        if (location == KeyEvent.KEY_LOCATION_NUMPAD)
+          keysym = Keysyms.KP_Right;
+        else
+          keysym = Keysyms.Right;  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;
@@ -1223,22 +1316,65 @@ public class CConn extends CConnection
       case KeyEvent.VK_F10:          keysym = Keysyms.F10; break;
       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_PRINTSCREEN:  keysym = Keysyms.Print; break;
-      case KeyEvent.VK_PAUSE:        keysym = Keysyms.Pause; break;
-      case KeyEvent.VK_INSERT:       keysym = Keysyms.Insert; break;
+      case KeyEvent.VK_PAUSE:
+        if (ev.isControlDown())
+          keysym = Keysyms.Break;
+        else
+          keysym = Keysyms.Pause;
+        break;
+      case KeyEvent.VK_INSERT:
+        if (location == KeyEvent.KEY_LOCATION_NUMPAD)
+          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;
+        else
+          keysym = Keysyms.Begin;  break;
       default: return;
       }
     }
 
-    writeModifiers(currentModifiers);
-    writeKeyEvent(keysym, true);
-    writeKeyEvent(keysym, false);
-    writeModifiers(0);
+    if (fakeModifiers != 0) {
+      if ((fakeModifiers & Event.CTRL_MASK) != 0) {
+        vlog.debug("Fake L Ctrl raised");
+        writeKeyEvent(Keysyms.Control_L, false);
+      }
+      if ((modifiers & Event.ALT_MASK) != 0) {
+        vlog.debug("Fake R Alt raised");
+        writeKeyEvent(Keysyms.Alt_R, false);
+      }
+    }
+    writeKeyEvent(keysym, down);
+    if (fakeModifiers != 0) {
+      if ((fakeModifiers & Event.CTRL_MASK) != 0) {
+        vlog.debug("Fake L Ctrl pressed");
+        writeKeyEvent(Keysyms.Control_L, true);
+      }
+      if ((modifiers & Event.ALT_MASK) != 0) {
+        vlog.debug("Fake R Alt pressed");
+        writeKeyEvent(Keysyms.Alt_R, true);
+      }
+      fakeModifiers = 0;
+    }
   }
 
 
   public void writePointerEvent(MouseEvent ev) {
-    if (state() != RFBSTATE_NORMAL) return;
+    if (state() != RFBSTATE_NORMAL || shuttingDown)
+      return;
 
     switch (ev.getID()) {
     case MouseEvent.MOUSE_PRESSED:
@@ -1251,25 +1387,22 @@ public class CConn extends CConnection
       break;
     }
 
-    writeModifiers(ev.getModifiers() & ~KeyEvent.ALT_MASK & ~KeyEvent.META_MASK);
-
-    if (cp.width != desktop.scaledWidth || 
+    if (cp.width != desktop.scaledWidth ||
         cp.height != desktop.scaledHeight) {
-      int sx = (desktop.scaleWidthRatio == 1.00) 
-        ? ev.getX() : (int)Math.floor(ev.getX()/desktop.scaleWidthRatio);
-      int sy = (desktop.scaleHeightRatio == 1.00) 
-        ? ev.getY() : (int)Math.floor(ev.getY()/desktop.scaleHeightRatio);
+      int sx = (desktop.scaleWidthRatio == 1.00) ?
+        ev.getX() : (int)Math.floor(ev.getX() / desktop.scaleWidthRatio);
+      int sy = (desktop.scaleHeightRatio == 1.00) ?
+        ev.getY() : (int)Math.floor(ev.getY() / desktop.scaleHeightRatio);
       ev.translatePoint(sx - ev.getX(), sy - ev.getY());
     }
-    
-    writer().writePointerEvent(new Point(ev.getX(),ev.getY()), buttonMask);
 
-    if (buttonMask == 0) writeModifiers(0);
+    writer().writePointerEvent(new Point(ev.getX(), ev.getY()), buttonMask);
   }
 
 
   public void writeWheelEvent(MouseWheelEvent ev) {
-    if (state() != RFBSTATE_NORMAL) return;
+    if (state() != RFBSTATE_NORMAL || shuttingDown)
+      return;
     int x, y;
     int clicks = ev.getWheelRotation();
     if (clicks < 0) {
@@ -1277,29 +1410,27 @@ public class CConn extends CConnection
     } else {
       buttonMask = 16;
     }
-    writeModifiers(ev.getModifiers() & ~KeyEvent.ALT_MASK & ~KeyEvent.META_MASK);
-    for (int i=0;i<Math.abs(clicks);i++) {
+    for (int i = 0; i < Math.abs(clicks); i++) {
       x = ev.getX();
       y = ev.getY();
       writer().writePointerEvent(new Point(x, y), buttonMask);
       buttonMask = 0;
       writer().writePointerEvent(new Point(x, y), buttonMask);
     }
-    writeModifiers(0);
 
   }
 
 
-  synchronized void writeModifiers(int m) {
-    if ((m & Event.SHIFT_MASK) != (pressedModifiers & Event.SHIFT_MASK))
-      writeKeyEvent(Keysyms.Shift_L, (m & Event.SHIFT_MASK) != 0);
-    if ((m & Event.CTRL_MASK) != (pressedModifiers & Event.CTRL_MASK))
-      writeKeyEvent(Keysyms.Control_L, (m & Event.CTRL_MASK) != 0);
-    if ((m & Event.ALT_MASK) != (pressedModifiers & Event.ALT_MASK))
-      writeKeyEvent(Keysyms.Alt_L, (m & Event.ALT_MASK) != 0);
-    if ((m & Event.META_MASK) != (pressedModifiers & Event.META_MASK))
-      writeKeyEvent(Keysyms.Meta_L, (m & Event.META_MASK) != 0);
-    pressedModifiers = m;
+  synchronized void releaseModifiers() {
+    if ((modifiers & Event.SHIFT_MASK) != 0)
+      writeKeyEvent(Keysyms.Shift_L, false);
+    if ((modifiers & Event.CTRL_MASK) != 0)
+      writeKeyEvent(Keysyms.Control_L, false);
+    if ((modifiers & Event.ALT_MASK) != 0)
+      writeKeyEvent(Keysyms.Alt_L, false);
+    if ((modifiers & Event.META_MASK) != 0)
+      writeKeyEvent(Keysyms.Meta_L, false);
+    modifiers = 0;
   }
 
 
@@ -1309,7 +1440,8 @@ public class CConn extends CConnection
   // checkEncodings() sends a setEncodings message if one is needed.
   private void checkEncodings() {
     if (encodingChange && (writer() != null)) {
-      vlog.info("Requesting "+Encodings.encodingName(currentEncoding)+" encoding");
+      vlog.info("Requesting " + Encodings.encodingName(currentEncoding) +
+        " encoding");
       writer().writeSetEncodings(currentEncoding, true);
       encodingChange = false;
     }
@@ -1350,7 +1482,6 @@ public class CConn extends CConnection
 
   // the following are only ever accessed by the GUI thread:
   int buttonMask;
-  int pressedModifiers;
 
   private String serverHost;
   private int serverPort;
@@ -1378,6 +1509,7 @@ public class CConn extends CConnection
 
   private boolean supportsSyncFence;
 
+  int modifiers;
   public int menuKeyCode;
   Viewport viewport;
   private boolean fullColour;
index 42772629cc56b8533e77c0e1206222866174ebf4..42a1cc9de01fb83bcfa850586f74c81531289331 100644 (file)
@@ -1,19 +1,19 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
  * Copyright (C) 2006 Constantin Kaplinsky.  All Rights Reserved.
  * Copyright (C) 2009 Paul Donohue.  All Rights Reserved.
- * Copyright (C) 2010, 2012 D. R. Commander.  All Rights Reserved.
+ * Copyright (C) 2010, 2012-2013 D. R. Commander.  All Rights Reserved.
  * Copyright (C) 2011-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,
@@ -41,18 +41,14 @@ import com.tigervnc.rfb.*;
 import com.tigervnc.rfb.Cursor;
 import com.tigervnc.rfb.Point;
 
-class DesktopWindow extends JPanel implements
-                                   Runnable,
-                                   MouseListener,
-                                   MouseMotionListener,
-                                   MouseWheelListener,
-                                   KeyListener
-{
+class DesktopWindow extends JPanel implements Runnable, MouseListener,
+  MouseMotionListener, MouseWheelListener, KeyListener {
 
   ////////////////////////////////////////////////////////////////////
   // The following methods are all called from the RFB thread
 
-  public DesktopWindow(int width, int height, PixelFormat serverPF, CConn cc_) {
+  public DesktopWindow(int width, int height, PixelFormat serverPF,
+                       CConn cc_) {
     cc = cc_;
     setSize(width, height);
     setOpaque(false);
@@ -78,6 +74,7 @@ class DesktopWindow extends JPanel implements
                                     BufferedImage.TYPE_INT_ARGB);
     java.awt.Point hotspot = new java.awt.Point(0,0);
     nullCursor = tk.createCustomCursor(cursorImage, hotspot, "nullCursor");
+    cursorImage.flush();
     if (!cc.cp.supportsLocalCursor && !bestSize.equals(new Dimension(0,0)))
       setCursor(nullCursor);
     addMouseListener(this);
@@ -88,11 +85,14 @@ class DesktopWindow extends JPanel implements
       public void focusGained(FocusEvent e) {
         checkClipboard();
       }
+      public void focusLost(FocusEvent e) {
+        cc.releaseModifiers();
+      }
     });
     setFocusTraversalKeysEnabled(false);
     setFocusable(true);
   }
-  
+
   public int width() {
     return getWidth();
   }
@@ -101,17 +101,16 @@ class DesktopWindow extends JPanel implements
     return getHeight();
   }
 
-  final public PixelFormat getPF() { return im.getPF(); }
+  public final PixelFormat getPF() { return im.getPF(); }
 
-  public void setViewport(Viewport viewport)
-  {
+  public void setViewport(Viewport viewport) {
     viewport.setChild(this);
   }
 
   // Methods called from the RFB thread - these need to be synchronized
   // wherever they access data shared with the GUI thread.
 
-  public void setCursor(int w, int h, Point hotspot, 
+  public void setCursor(int w, int h, Point hotspot,
                         int[] data, byte[] mask) {
     // strictly we should use a mutex around this test since useLocalCursor
     // might be being altered by the GUI thread.  However it's only a single
@@ -146,7 +145,7 @@ class DesktopWindow extends JPanel implements
             (im.cm.getBlue(data[y * w + x]));
         }
       }
-      System.arraycopy(mask, y * maskBytesPerRow, cursor.mask, 
+      System.arraycopy(mask, y * maskBytesPerRow, cursor.mask,
         y * ((cursor.width() + 7) / 8), maskBytesPerRow);
     }
 
@@ -158,7 +157,7 @@ class DesktopWindow extends JPanel implements
                                       ColorModel.getRGBdefault(),
                                       cursor.data, 0, cursor.width());
     Image srcImage = tk.createImage(cursorSrc);
-    BufferedImage cursorImage; 
+    BufferedImage cursorImage;
     cursorImage = new BufferedImage(bestSize.width, bestSize.height, 
                                     BufferedImage.TYPE_INT_ARGB);
     Graphics2D g2 = cursorImage.createGraphics();
@@ -166,21 +165,21 @@ class DesktopWindow extends JPanel implements
                         RenderingHints.VALUE_RENDER_SPEED);
     g2.drawImage(srcImage, 0, 0, (int)Math.min(cw, bestSize.width),
                  (int)Math.min(ch, bestSize.height), 0, 0, cursor.width(),
-                 cursor.height(), null) ;
+                 cursor.height(), null);
     g2.dispose();
     srcImage.flush();
 
     int x = (int)Math.floor((float)cursor.hotspot.x * scaleWidthRatio);
     int y = (int)Math.floor((float)cursor.hotspot.y * scaleHeightRatio);
-    x = (int)Math.min(x, Math.max(bestSize.width-1, 0));
-    y = (int)Math.min(y, Math.max(bestSize.height-1, 0));
+    x = (int)Math.min(x, Math.max(bestSize.width - 1, 0));
+    y = (int)Math.min(y, Math.max(bestSize.height - 1, 0));
     java.awt.Point hs = new java.awt.Point(x, y);
-    if (!bestSize.equals(new Dimension(0,0)))
+    if (!bestSize.equals(new Dimension(0, 0)))
       softCursor = tk.createCustomCursor(cursorImage, hs, "softCursor");
     cursorImage.flush();
 
     if (softCursor != null) {
-      setCursor(softCursor); 
+      setCursor(softCursor);
       cursorAvailable = false;
       return;
     }
@@ -206,7 +205,7 @@ class DesktopWindow extends JPanel implements
   // because getting java to recalculate its internal translation table and
   // redraw the screen is expensive.
 
-  synchronized public void setColourMapEntries(int firstColour, int nColours,
+  public synchronized void setColourMapEntries(int firstColour, int nColours,
                                                int[] rgbs) {
     im.setColourMapEntries(firstColour, nColours, rgbs);
     if (nColours <= 256) {
@@ -220,8 +219,7 @@ class DesktopWindow extends JPanel implements
   }
 
   // Update the actual window with the changed parts of the framebuffer.
-  public void updateWindow()
-  {
+  public void updateWindow() {
     Rect r = damage;
     if (!r.is_empty()) {
       if (cc.cp.width != scaledWidth || cc.cp.height != scaledHeight) {
@@ -247,24 +245,23 @@ class DesktopWindow extends JPanel implements
     im.resize(w, h);
   }
 
-  final public void fillRect(int x, int y, int w, int h, int pix)
-  {
+  public final void fillRect(int x, int y, int w, int h, int pix) {
     if (overlapsCursor(x, y, w, h)) hideLocalCursor();
     im.fillRect(x, y, w, h, pix);
     damageRect(new Rect(x, y, x+w, y+h));
     showLocalCursor();
   }
 
-  final public void imageRect(int x, int y, int w, int h,
-                                           Object pix) {
+  public final void imageRect(int x, int y, int w, int h,
+                              Object pix) {
     if (overlapsCursor(x, y, w, h)) hideLocalCursor();
     im.imageRect(x, y, w, h, pix);
     damageRect(new Rect(x, y, x+w, y+h));
     showLocalCursor();
   }
 
-  final public void copyRect(int x, int y, int w, int h,
-                                          int srcX, int srcY) {
+  public final void copyRect(int x, int y, int w, int h,
+                             int srcX, int srcY) {
     if (overlapsCursor(x, y, w, h) || overlapsCursor(srcX, srcY, w, h))
       hideLocalCursor();
     im.copyRect(x, y, w, h, srcX, srcY);
@@ -277,7 +274,7 @@ class DesktopWindow extends JPanel implements
   final boolean overlapsCursor(int x, int y, int w, int h) {
     return (x < cursorBackingX + cursorBacking.width() &&
             y < cursorBackingY + cursorBacking.height() &&
-            x+w > cursorBackingX && y+h > cursorBackingY);
+            x + w > cursorBackingX && y + h > cursorBackingY);
   }
 
 
@@ -353,16 +350,16 @@ class DesktopWindow extends JPanel implements
     if (cc.cp.width != scaledWidth || cc.cp.height != scaledHeight) {
       g2.setRenderingHint(RenderingHints.KEY_RENDERING,
                           RenderingHints.VALUE_RENDER_QUALITY);
-      g2.drawImage(im.getImage(), 0, 0, scaledWidth, scaledHeight, null);  
+      g2.drawImage(im.getImage(), 0, 0, scaledWidth, scaledHeight, null);
     } else {
       g2.drawImage(im.getImage(), 0, 0, null);
     }
     g2.dispose();
   }
-  
+
   String oldContents = "";
-  
-  synchronized public void checkClipboard() {
+
+  public synchronized void checkClipboard() {
     SecurityManager sm = System.getSecurityManager();
     try {
       if (sm != null) sm.checkSystemClipboardAccess();
@@ -377,7 +374,7 @@ class DesktopWindow extends JPanel implements
               oldContents = newContents;
               cc.clipboardDialog.setContents(newContents);
             }
-          } catch (java.lang.Exception e) {
+          } catch(java.lang.Exception e) {
             System.out.println("Exception getting clipboard data: " + e.getMessage());
           }
         }
@@ -387,7 +384,7 @@ class DesktopWindow extends JPanel implements
     }
   }
 
-  /** Mouse-Motion callback function */
+  // Mouse-Motion callback function
   private void mouseMotionCB(MouseEvent e) {
     if (!cc.viewer.viewOnly.getValue() &&
         e.getX() >= 0 && e.getX() <= scaledWidth &&
@@ -407,12 +404,12 @@ class DesktopWindow extends JPanel implements
       }
     }
     lastX = e.getX();
-    lastY = e.getY();      
+    lastY = e.getY();
   }
-  public void mouseDragged(MouseEvent e) { mouseMotionCB(e);}
-  public void mouseMoved(MouseEvent e) { mouseMotionCB(e);}
+  public void mouseDragged(MouseEvent e) { mouseMotionCB(e); }
+  public void mouseMoved(MouseEvent e) { mouseMotionCB(e); }
 
-  /** Mouse callback function */
+  // Mouse callback function
   private void mouseCB(MouseEvent e) {
     if (!cc.viewer.viewOnly.getValue()) {
       if ((e.getID() == MouseEvent.MOUSE_RELEASED) ||
@@ -423,37 +420,82 @@ class DesktopWindow extends JPanel implements
     lastX = e.getX();
     lastY = e.getY();
   }
-  public void mouseReleased(MouseEvent e){ mouseCB(e);}
-  public void mousePressed(MouseEvent e) { mouseCB(e);}
-  public void mouseClicked(MouseEvent e){}
-  public void mouseEntered(MouseEvent e){}
-  public void mouseExited(MouseEvent e){}  
-  
-  /** MouseWheel callback function */
+  public void mouseReleased(MouseEvent e) { mouseCB(e); }
+  public void mousePressed(MouseEvent e) { mouseCB(e); }
+  public void mouseClicked(MouseEvent e) {}
+  public void mouseEntered(MouseEvent e) {}
+  public void mouseExited(MouseEvent e) {}
+
+  // MouseWheel callback function
   private void mouseWheelCB(MouseWheelEvent e) {
     if (!cc.viewer.viewOnly.getValue())
       cc.writeWheelEvent(e);
   }
-  public void mouseWheelMoved(MouseWheelEvent e){ 
+
+  public void mouseWheelMoved(MouseWheelEvent e) {
     mouseWheelCB(e);
   }
 
-  /** Handle the key-typed event. */
+  // Handle the key-typed event.
   public void keyTyped(KeyEvent e) {}
-  /** Handle the key-released event. */
-  public void keyReleased(KeyEvent e) {}
-  /** Handle the key-pressed event. */
+
+  // Handle the key-released event.
+  public void keyReleased(KeyEvent e) {
+    if (!cc.viewer.viewOnly.getValue())
+      cc.writeKeyEvent(e);
+  }
+
+  // Handle the key-pressed event.
   public void keyPressed(KeyEvent e) {
     if (e.getKeyCode() == MenuKey.getMenuKeyCode()) {
-      int sx = (scaleWidthRatio == 1.00) 
-        ? lastX : (int)Math.floor(lastX*scaleWidthRatio);
-      int sy = (scaleHeightRatio == 1.00) 
-        ? lastY : (int)Math.floor(lastY*scaleHeightRatio);
+      int sx = (scaleWidthRatio == 1.00) ?
+        lastX : (int)Math.floor(lastX * scaleWidthRatio);
+      int sy = (scaleHeightRatio == 1.00) ?
+        lastY : (int)Math.floor(lastY * scaleHeightRatio);
       java.awt.Point ev = new java.awt.Point(lastX, lastY);
       ev.translate(sx - lastX, sy - lastY);
       cc.showMenu((int)ev.getX(), (int)ev.getY());
       return;
     }
+    int ctrlAltShiftMask = Event.SHIFT_MASK | Event.CTRL_MASK | Event.ALT_MASK;
+    if ((e.getModifiers() & ctrlAltShiftMask) == ctrlAltShiftMask) {
+      switch (e.getKeyCode()) {
+        case KeyEvent.VK_F:
+          cc.toggleFullScreen();
+          return;
+        case KeyEvent.VK_I:
+          cc.showInfo();
+          return;
+        case KeyEvent.VK_N:
+          VncViewer.newViewer(cc.viewer);
+          return;
+        case KeyEvent.VK_O:
+          cc.options.showDialog(cc.viewport);
+          return;
+        case KeyEvent.VK_R:
+          cc.refresh();
+          return;
+        case KeyEvent.VK_LEFT:
+        case KeyEvent.VK_RIGHT:
+        case KeyEvent.VK_UP:
+        case KeyEvent.VK_DOWN:
+          return;
+      }
+    }
+    if ((e.getModifiers() & Event.META_MASK) == Event.META_MASK) {
+      switch (e.getKeyCode()) {
+        case KeyEvent.VK_COMMA:
+        case KeyEvent.VK_N:
+        case KeyEvent.VK_W:
+        case KeyEvent.VK_I:
+        case KeyEvent.VK_R:
+        case KeyEvent.VK_L:
+        case KeyEvent.VK_F:
+        case KeyEvent.VK_Z:
+        case KeyEvent.VK_T:
+          return;
+      }
+    }
     if (!cc.viewer.viewOnly.getValue())
       cc.writeKeyEvent(e);
   }
@@ -464,7 +506,7 @@ class DesktopWindow extends JPanel implements
   // Note that mutex MUST be held when hideLocalCursor() and showLocalCursor()
   // are called.
 
-  synchronized private void hideLocalCursor() {
+  private synchronized void hideLocalCursor() {
     // - Blit the cursor backing store over the cursor
     if (cursorVisible) {
       cursorVisible = false;
@@ -476,7 +518,7 @@ class DesktopWindow extends JPanel implements
     }
   }
 
-  synchronized private void showLocalCursor() {
+  private synchronized void showLocalCursor() {
     if (cursorAvailable && !cursorVisible) {
       if (!im.getPF().equal(cursor.getPF()) ||
           cursor.width() == 0 || cursor.height() == 0) {
@@ -499,10 +541,10 @@ class DesktopWindow extends JPanel implements
       cursorBackingX = x;
       cursorBackingY = y;
       cursorBacking.setSize(w, h);
-      
+
       for (int j = 0; j < h; j++)
-        System.arraycopy(im.data, (y+j) * im.width() + x,
-                         cursorBacking.data, j*w, w);
+        System.arraycopy(im.data, (y + j) * im.width() + x,
+                         cursorBacking.data, j * w, w);
 
       im.maskRect(cursorLeft, cursorTop, cursor.width(), cursor.height(),
                   cursor.data, cursor.mask);
@@ -521,10 +563,10 @@ class DesktopWindow extends JPanel implements
 
   // run() is executed by the setColourMapEntriesTimerThread - it sleeps for
   // 100ms before actually updating the colourmap.
-  synchronized public void run() {
+  public synchronized void run() {
     try {
       Thread.sleep(100);
-    } catch (InterruptedException e) {}
+    } catch(InterruptedException e) {}
     im.updateColourMap();
     setColourMapEntriesTimerThread = null;
   }