summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Hinz <bphinz@users.sourceforge.net>2013-03-03 16:53:41 +0000
committerBrian Hinz <bphinz@users.sourceforge.net>2013-03-03 16:53:41 +0000
commit776c558c442ad3e6cb54a4266d970f3c734e59d1 (patch)
tree843e650e3ea45df4f1344314182c5505b15dca80
parentdf03a901f1c268f6f4d017a4c99c714eb99c5a58 (diff)
downloadtigervnc-776c558c442ad3e6cb54a4266d970f3c734e59d1.tar.gz
tigervnc-776c558c442ad3e6cb54a4266d970f3c734e59d1.zip
Backported a number of fixes/enhancements (mostly related to keymapping) from TurboVNC viewer. Also cleaned up some whitespace and minor formatting changes.
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@5056 3789f03b-4d11-0410-bbf8-ca57d06f2519
-rw-r--r--java/com/tigervnc/rfb/Keysyms.java39
-rw-r--r--java/com/tigervnc/vncviewer/CConn.java370
-rw-r--r--java/com/tigervnc/vncviewer/DesktopWindow.java176
3 files changed, 396 insertions, 189 deletions
diff --git a/java/com/tigervnc/rfb/Keysyms.java b/java/com/tigervnc/rfb/Keysyms.java
index 5e1bf954..93aea127 100644
--- a/java/com/tigervnc/rfb/Keysyms.java
+++ b/java/com/tigervnc/rfb/Keysyms.java
@@ -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;
}
diff --git a/java/com/tigervnc/vncviewer/CConn.java b/java/com/tigervnc/vncviewer/CConn.java
index ce858c9f..0a7526ee 100644
--- a/java/com/tigervnc/vncviewer/CConn.java
+++ b/java/com/tigervnc/vncviewer/CConn.java
@@ -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;
diff --git a/java/com/tigervnc/vncviewer/DesktopWindow.java b/java/com/tigervnc/vncviewer/DesktopWindow.java
index 42772629..42a1cc9d 100644
--- a/java/com/tigervnc/vncviewer/DesktopWindow.java
+++ b/java/com/tigervnc/vncviewer/DesktopWindow.java
@@ -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;
}