diff options
author | Brian Hinz <bphinz@users.sourceforge.net> | 2013-03-03 16:53:41 +0000 |
---|---|---|
committer | Brian Hinz <bphinz@users.sourceforge.net> | 2013-03-03 16:53:41 +0000 |
commit | 776c558c442ad3e6cb54a4266d970f3c734e59d1 (patch) | |
tree | 843e650e3ea45df4f1344314182c5505b15dca80 | |
parent | df03a901f1c268f6f4d017a4c99c714eb99c5a58 (diff) | |
download | tigervnc-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.java | 39 | ||||
-rw-r--r-- | java/com/tigervnc/vncviewer/CConn.java | 370 | ||||
-rw-r--r-- | java/com/tigervnc/vncviewer/DesktopWindow.java | 176 |
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; } |