diff options
author | Brian Hinz <bphinz@users.sourceforge.net> | 2012-02-12 20:44:29 +0000 |
---|---|---|
committer | Brian Hinz <bphinz@users.sourceforge.net> | 2012-02-12 20:44:29 +0000 |
commit | af15db239a665030087fe146b20d4b3213708f3d (patch) | |
tree | 2efad69694d73cdb8fc90af86af26ef44e10b084 /java | |
parent | 8d37f2054b6da02957b12d02aaf96dd5eecc89c7 (diff) | |
download | tigervnc-af15db239a665030087fe146b20d4b3213708f3d.tar.gz tigervnc-af15db239a665030087fe146b20d4b3213708f3d.zip |
Adds support for fence & continuous updates extensions to java viewer. Adds low level hooks for TurboVNC fine grained quality controls.
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4847 3789f03b-4d11-0410-bbf8-ca57d06f2519
Diffstat (limited to 'java')
-rw-r--r-- | java/com/tigervnc/network/SocketDescriptor.java | 6 | ||||
-rw-r--r-- | java/com/tigervnc/rdr/Exception.java | 1 | ||||
-rw-r--r-- | java/com/tigervnc/rdr/MemInStream.java | 8 | ||||
-rw-r--r-- | java/com/tigervnc/rdr/MemOutStream.java | 4 | ||||
-rw-r--r-- | java/com/tigervnc/rfb/CMsgHandler.java | 10 | ||||
-rw-r--r-- | java/com/tigervnc/rfb/CMsgReaderV3.java | 29 | ||||
-rw-r--r-- | java/com/tigervnc/rfb/CMsgWriter.java | 7 | ||||
-rw-r--r-- | java/com/tigervnc/rfb/CMsgWriterV3.java | 39 | ||||
-rw-r--r-- | java/com/tigervnc/rfb/ConnParams.java | 44 | ||||
-rw-r--r-- | java/com/tigervnc/rfb/Encodings.java | 14 | ||||
-rw-r--r-- | java/com/tigervnc/rfb/JpegCompressor.java | 48 | ||||
-rw-r--r-- | java/com/tigervnc/rfb/MsgTypes.java | 9 | ||||
-rw-r--r-- | java/com/tigervnc/rfb/PixelBuffer.java | 12 | ||||
-rw-r--r-- | java/com/tigervnc/rfb/fenceTypes.java | 31 | ||||
-rw-r--r-- | java/com/tigervnc/vncviewer/CConn.java | 273 | ||||
-rw-r--r-- | java/com/tigervnc/vncviewer/DesktopWindow.java | 19 | ||||
-rw-r--r-- | java/com/tigervnc/vncviewer/PixelBufferImage.java | 18 | ||||
-rw-r--r-- | java/com/tigervnc/vncviewer/VncViewer.java | 2 |
18 files changed, 467 insertions, 107 deletions
diff --git a/java/com/tigervnc/network/SocketDescriptor.java b/java/com/tigervnc/network/SocketDescriptor.java index 1998ca5b..d4970c61 100644 --- a/java/com/tigervnc/network/SocketDescriptor.java +++ b/java/com/tigervnc/network/SocketDescriptor.java @@ -49,7 +49,7 @@ public class SocketDescriptor extends SocketChannel } } - public int read(byte[] buf, int bufPtr, int length) throws Exception { + synchronized public int read(byte[] buf, int bufPtr, int length) throws Exception { int n; ByteBuffer b = ByteBuffer.allocate(length); try { @@ -66,7 +66,7 @@ public class SocketDescriptor extends SocketChannel } - public int write(byte[] buf, int bufPtr, int length) throws Exception { + synchronized public int write(byte[] buf, int bufPtr, int length) throws Exception { int n; ByteBuffer b = ByteBuffer.allocate(length); b.put(buf, bufPtr, length); @@ -80,7 +80,7 @@ public class SocketDescriptor extends SocketChannel return n; } - public int select(int interestOps, int timeout) throws Exception { + synchronized public int select(int interestOps, int timeout) throws Exception { int n; try { n = selector.select(timeout); diff --git a/java/com/tigervnc/rdr/Exception.java b/java/com/tigervnc/rdr/Exception.java index a2f8833d..2187685e 100644 --- a/java/com/tigervnc/rdr/Exception.java +++ b/java/com/tigervnc/rdr/Exception.java @@ -21,6 +21,7 @@ package com.tigervnc.rdr; public class Exception extends RuntimeException { public Exception(String s) { super(s); + System.out.println(s); } } diff --git a/java/com/tigervnc/rdr/MemInStream.java b/java/com/tigervnc/rdr/MemInStream.java index 32911a3a..6a768aad 100644 --- a/java/com/tigervnc/rdr/MemInStream.java +++ b/java/com/tigervnc/rdr/MemInStream.java @@ -22,13 +22,17 @@ public class MemInStream extends InStream { public MemInStream(byte[] data, int offset, int len) { b = data; - ptr = offset; - end = offset + len; + start = offset; + ptr = start; + end = start + len; } public int pos() { return ptr; } + public void reposition(int pos) { ptr = start + pos; } protected int overrun(int itemSize, int nItems, boolean wait) { throw new EndOfStream(); } + + int start; } diff --git a/java/com/tigervnc/rdr/MemOutStream.java b/java/com/tigervnc/rdr/MemOutStream.java index b3040793..4d654a2b 100644 --- a/java/com/tigervnc/rdr/MemOutStream.java +++ b/java/com/tigervnc/rdr/MemOutStream.java @@ -35,6 +35,10 @@ public class MemOutStream extends OutStream { public void clear() { ptr = 0; }; public void reposition(int pos) { ptr = pos; } + // data() returns a pointer to the buffer. + + public final byte[] data() { return b; } + // overrun() either doubles the buffer or adds enough space for nItems of // size itemSize bytes. diff --git a/java/com/tigervnc/rfb/CMsgHandler.java b/java/com/tigervnc/rfb/CMsgHandler.java index 31182003..533bfbc2 100644 --- a/java/com/tigervnc/rfb/CMsgHandler.java +++ b/java/com/tigervnc/rfb/CMsgHandler.java @@ -61,6 +61,16 @@ abstract public class CMsgHandler { cp.setName(name); } + public void fence(int flags, int len, byte[] data) + { + cp.supportsFence = true; + } + + public void endOfContinuousUpdates() + { + cp.supportsContinuousUpdates = true; + } + public void clientRedirect(int port, String host, String x509subject) {} diff --git a/java/com/tigervnc/rfb/CMsgReaderV3.java b/java/com/tigervnc/rfb/CMsgReaderV3.java index 6d9e254b..915b1e01 100644 --- a/java/com/tigervnc/rfb/CMsgReaderV3.java +++ b/java/com/tigervnc/rfb/CMsgReaderV3.java @@ -51,6 +51,8 @@ public class CMsgReaderV3 extends CMsgReader { case MsgTypes.msgTypeSetColourMapEntries: readSetColourMapEntries(); break; case MsgTypes.msgTypeBell: readBell(); break; case MsgTypes.msgTypeServerCutText: readServerCutText(); break; + case MsgTypes.msgTypeServerFence: readFence(); break; + case MsgTypes.msgTypeEndOfContinuousUpdates: readEndOfContinuousUpdates(); break; default: vlog.error("unknown message type "+type); throw new Exception("unknown message type"); @@ -136,6 +138,33 @@ public class CMsgReaderV3 extends CMsgReader { handler.setExtendedDesktopSize(x, y, w, h, layout); } + void readFence() + { + int flags; + int len; + byte[] data = new byte[64]; + + is.skip(3); + + flags = is.readU32(); + + len = is.readU8(); + if (len > data.length) { + System.out.println("Ignoring fence with too large payload\n"); + is.skip(len); + return; + } + + is.readBytes(data, 0, len); + + handler.fence(flags, len, data); + } + + void readEndOfContinuousUpdates() + { + handler.endOfContinuousUpdates(); + } + void readClientRedirect(int x, int y, int w, int h) { int port = is.readU16(); diff --git a/java/com/tigervnc/rfb/CMsgWriter.java b/java/com/tigervnc/rfb/CMsgWriter.java index 7cafddde..c4f43554 100644 --- a/java/com/tigervnc/rfb/CMsgWriter.java +++ b/java/com/tigervnc/rfb/CMsgWriter.java @@ -59,9 +59,16 @@ abstract public class CMsgWriter { encodings[nEncodings++] = Encodings.pseudoEncodingDesktopName; if (cp.supportsClientRedirect) encodings[nEncodings++] = Encodings.pseudoEncodingClientRedirect; + + encodings[nEncodings++] = Encodings.pseudoEncodingLastRect; + encodings[nEncodings++] = Encodings.pseudoEncodingContinuousUpdates; + encodings[nEncodings++] = Encodings.pseudoEncodingFence; + + if (Decoder.supported(preferredEncoding)) { encodings[nEncodings++] = preferredEncoding; } + if (useCopyRect) { encodings[nEncodings++] = Encodings.encodingCopyRect; } diff --git a/java/com/tigervnc/rfb/CMsgWriterV3.java b/java/com/tigervnc/rfb/CMsgWriterV3.java index ec30a826..430c3746 100644 --- a/java/com/tigervnc/rfb/CMsgWriterV3.java +++ b/java/com/tigervnc/rfb/CMsgWriterV3.java @@ -1,4 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2009-2011 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -65,4 +66,42 @@ public class CMsgWriterV3 extends CMsgWriter { endMsg(); } + + public void writeFence(int flags, int len, byte[] data) + { + if (!cp.supportsFence) + throw new Exception("Server does not support fences"); + if (len > 64) + throw new Exception("Too large fence payload"); + if ((flags & ~fenceTypes.fenceFlagsSupported) != 0) + throw new Exception("Unknown fence flags"); + + startMsg(MsgTypes.msgTypeClientFence); + os.pad(3); + + os.writeU32(flags); + + os.writeU8(len); + os.writeBytes(data, 0, len); + + endMsg(); + } + + public void writeEnableContinuousUpdates(boolean enable, + int x, int y, int w, int h) + { + if (!cp.supportsContinuousUpdates) + throw new Exception("Server does not support continuous updates"); + + startMsg(MsgTypes.msgTypeEnableContinuousUpdates); + + os.writeU8((enable?1:0)); + + os.writeU16(x); + os.writeU16(y); + os.writeU16(w); + os.writeU16(h); + + endMsg(); + } } diff --git a/java/com/tigervnc/rfb/ConnParams.java b/java/com/tigervnc/rfb/ConnParams.java index 70d6114f..7fac1260 100644 --- a/java/com/tigervnc/rfb/ConnParams.java +++ b/java/com/tigervnc/rfb/ConnParams.java @@ -1,4 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2012 TigerVNC Team. * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,9 +30,12 @@ public class ConnParams { supportsLocalCursor = false; supportsLocalXCursor = false; supportsDesktopResize = false; supportsExtendedDesktopSize = false; supportsDesktopRename = false; supportsLastRect = false; - supportsSetDesktopSize = false; supportsClientRedirect = false; + supportsSetDesktopSize = false; supportsFence = false; + supportsContinuousUpdates = false; + supportsClientRedirect = false; customCompressLevel = false; compressLevel = 6; - noJpeg = false; qualityLevel = -1; + noJpeg = false; qualityLevel = -1; fineQualityLevel = -1; + subsampling = "SUBSAMP_UNDEFINED"; name_ = null; nEncodings_ = 0; encodings_ = null; currentEncoding_ = Encodings.encodingRaw; verStrPos = 0; screenLayout = new ScreenSet(); @@ -115,20 +119,38 @@ public class ConnParams { useCopyRect = false; supportsLocalCursor = false; supportsDesktopResize = false; + supportsExtendedDesktopSize = false; + supportsLocalXCursor = false; + supportsLastRect = false; customCompressLevel = false; compressLevel = -1; noJpeg = true; qualityLevel = -1; + fineQualityLevel = -1; + subsampling = "SUBSAMP_UNDEFINED"; currentEncoding_ = Encodings.encodingRaw; for (int i = nEncodings-1; i >= 0; i--) { encodings_[i] = encodings[i]; + if (encodings[i] == Encodings.encodingCopyRect) useCopyRect = true; else if (encodings[i] == Encodings.pseudoEncodingCursor) supportsLocalCursor = true; + else if (encodings[i] == Encodings.pseudoEncodingXCursor) + supportsLocalXCursor = true; else if (encodings[i] == Encodings.pseudoEncodingDesktopSize) supportsDesktopResize = true; + else if (encodings[i] == Encodings.pseudoEncodingExtendedDesktopSize) + supportsExtendedDesktopSize = true; + else if (encodings[i] == Encodings.pseudoEncodingDesktopName) + supportsDesktopRename = true; + else if (encodings[i] == Encodings.pseudoEncodingLastRect) + supportsLastRect = true; + else if (encodings[i] == Encodings.pseudoEncodingFence) + supportsFence = true; + else if (encodings[i] == Encodings.pseudoEncodingContinuousUpdates) + supportsContinuousUpdates = true; else if (encodings[i] == Encodings.pseudoEncodingClientRedirect) supportsClientRedirect = true; else if (encodings[i] >= Encodings.pseudoEncodingCompressLevel0 && @@ -143,6 +165,20 @@ public class ConnParams { Encoder.supported(encodings[i])) currentEncoding_ = encodings[i]; } + + // If the TurboVNC fine quality/subsampling encodings exist, let them + // override the coarse TightVNC quality level + for (int i = nEncodings-1; i >= 0; i--) { + if (encodings[i] >= Encodings.pseudoEncodingFineQualityLevel0 + 1 && + encodings[i] <= Encodings.pseudoEncodingFineQualityLevel100) { + noJpeg = false; + fineQualityLevel = encodings[i] - Encodings.pseudoEncodingFineQualityLevel0; + } else if (encodings[i] >= Encodings.pseudoEncodingSubsamp1X && + encodings[i] <= Encodings.pseudoEncodingSubsampGray) { + noJpeg = false; + subsampling = JpegCompressor.subsamplingName(encodings[i] - Encodings.pseudoEncodingSubsamp1X); + } + } } public boolean useCopyRect; @@ -152,6 +188,8 @@ public class ConnParams { public boolean supportsExtendedDesktopSize; public boolean supportsDesktopRename; public boolean supportsClientRedirect; + public boolean supportsFence; + public boolean supportsContinuousUpdates; public boolean supportsLastRect; public boolean supportsSetDesktopSize; @@ -160,6 +198,8 @@ public class ConnParams { public int compressLevel; public boolean noJpeg; public int qualityLevel; + public int fineQualityLevel; + public String subsampling; private PixelFormat pf_; private String name_; diff --git a/java/com/tigervnc/rfb/Encodings.java b/java/com/tigervnc/rfb/Encodings.java index 493d5488..4dc129f4 100644 --- a/java/com/tigervnc/rfb/Encodings.java +++ b/java/com/tigervnc/rfb/Encodings.java @@ -1,4 +1,6 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2011 D. R. Commander. All Rights Reserved. + * Copyright (C) 2012 TigerVNC Team. * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,6 +38,8 @@ public class Encodings { public static final int pseudoEncodingExtendedDesktopSize = -308; public static final int pseudoEncodingDesktopName = -307; public static final int pseudoEncodingClientRedirect = -311; + public static final int pseudoEncodingFence = -312; + public static final int pseudoEncodingContinuousUpdates = -313; // TightVNC-specific public static final int pseudoEncodingLastRect = -224; @@ -44,6 +48,16 @@ public class Encodings { public static final int pseudoEncodingCompressLevel0 = -256; public static final int pseudoEncodingCompressLevel9 = -247; + // TurboVNC-specific + public static final int pseudoEncodingFineQualityLevel0 = -512; + public static final int pseudoEncodingFineQualityLevel100 = -412; + public static final int pseudoEncodingSubsamp1X = -768; + public static final int pseudoEncodingSubsamp4X = -767; + public static final int pseudoEncodingSubsamp2X = -766; + public static final int pseudoEncodingSubsampGray = -765; + public static final int pseudoEncodingSubsamp8X = -764; + public static final int pseudoEncodingSubsamp16X = -763; + public static int encodingNum(String name) { if (name.equalsIgnoreCase("raw")) return encodingRaw; if (name.equalsIgnoreCase("copyRect")) return encodingCopyRect; diff --git a/java/com/tigervnc/rfb/JpegCompressor.java b/java/com/tigervnc/rfb/JpegCompressor.java new file mode 100644 index 00000000..929aa4e0 --- /dev/null +++ b/java/com/tigervnc/rfb/JpegCompressor.java @@ -0,0 +1,48 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2011 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +package com.tigervnc.rfb; + +public class JpegCompressor { + + public static final int SUBSAMP_UNDEFINED = -1; + public static final int SUBSAMP_NONE = 0; + public static final int SUBSAMP_420 = 1; + public static final int SUBSAMP_422 = 2; + public static final int SUBSAMP_GRAY = 3; + + public static int subsamplingNum(String name) { + if (name.equalsIgnoreCase("SUBSAMP_UNDEFINED")) return SUBSAMP_UNDEFINED; + if (name.equalsIgnoreCase("SUBSAMP_NONE")) return SUBSAMP_NONE; + if (name.equalsIgnoreCase("SUBSAMP_420")) return SUBSAMP_420; + if (name.equalsIgnoreCase("SUBSAMP_422")) return SUBSAMP_422; + if (name.equalsIgnoreCase("SUBSAMP_GRAY")) return SUBSAMP_GRAY; + return SUBSAMP_UNDEFINED; + } + + public static String subsamplingName(int num) { + switch (num) { + case SUBSAMP_UNDEFINED: return "SUBSAMP_UNDEFINED"; + case SUBSAMP_NONE: return "SUBSAMP_NONE"; + case SUBSAMP_420: return "SUBSAMP_420"; + case SUBSAMP_422: return "SUBSAMP_422"; + case SUBSAMP_GRAY: return "SUBSAMP_GRAY"; + default: return "SUBSAMP_UNDEFINED"; + } + } +} diff --git a/java/com/tigervnc/rfb/MsgTypes.java b/java/com/tigervnc/rfb/MsgTypes.java index a009b396..2d452c81 100644 --- a/java/com/tigervnc/rfb/MsgTypes.java +++ b/java/com/tigervnc/rfb/MsgTypes.java @@ -1,4 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2012 TigerVNC Team. * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +27,10 @@ public class MsgTypes { public static final int msgTypeBell = 2; public static final int msgTypeServerCutText = 3; + public static final int msgTypeEndOfContinuousUpdates = 150; + + public static final int msgTypeServerFence = 248; + // client to server public static final int msgTypeSetPixelFormat = 0; @@ -36,5 +41,9 @@ public class MsgTypes { public static final int msgTypePointerEvent = 5; public static final int msgTypeClientCutText = 6; + public static final int msgTypeEnableContinuousUpdates = 150; + + public static final int msgTypeClientFence = 248; + public static final int msgTypeSetDesktopSize = 251; } diff --git a/java/com/tigervnc/rfb/PixelBuffer.java b/java/com/tigervnc/rfb/PixelBuffer.java index 2712ba94..43713550 100644 --- a/java/com/tigervnc/rfb/PixelBuffer.java +++ b/java/com/tigervnc/rfb/PixelBuffer.java @@ -36,9 +36,17 @@ public class PixelBuffer { throw new Exception("Internal error: bpp must be 8, 16, or 32 in PixelBuffer ("+pf.bpp+")"); format = pf; switch (pf.depth) { + case 3: + // Fall-through to depth 8 + case 6: + // Fall-through to depth 8 case 8: - //cm = new IndexColorModel(8, 256, new byte[256], new byte[256], new byte[256]); - cm = new DirectColorModel(8, 7, (7 << 3), (3 << 6)); + int rmask = pf.redMax << pf.redShift; + int gmask = pf.greenMax << pf.greenShift; + int bmask = pf.blueMax << pf.blueShift; + cm = new DirectColorModel(8, rmask, gmask, bmask); + if (pf.depth == 8 && !pf.trueColour) + cm = new IndexColorModel(8, 256, new byte[256], new byte[256], new byte[256]); break; case 16: cm = new DirectColorModel(32, 0xF800, 0x07C0, 0x003E, (0xff << 24)); diff --git a/java/com/tigervnc/rfb/fenceTypes.java b/java/com/tigervnc/rfb/fenceTypes.java new file mode 100644 index 00000000..a8e32d3b --- /dev/null +++ b/java/com/tigervnc/rfb/fenceTypes.java @@ -0,0 +1,31 @@ +/* Copyright 2011 Pierre Ossman for Cendio AB + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +package com.tigervnc.rfb; + +public class fenceTypes { + public static final int fenceFlagBlockBefore = 1<<0; + public static final int fenceFlagBlockAfter = 1<<1; + public static final int fenceFlagSyncNext = 1<<2; + + public static final int fenceFlagRequest = 1<<31; + + public static final int fenceFlagsSupported = (fenceFlagBlockBefore | + fenceFlagBlockAfter | + fenceFlagSyncNext | + fenceFlagRequest); +} diff --git a/java/com/tigervnc/vncviewer/CConn.java b/java/com/tigervnc/vncviewer/CConn.java index fd8bb624..8c45ecd8 100644 --- a/java/com/tigervnc/vncviewer/CConn.java +++ b/java/com/tigervnc/vncviewer/CConn.java @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright (C) 2012 TigerVNC Team + * Copyright (C) 2011-2012 TigerVNC Team * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -147,27 +147,38 @@ class ViewportFrame extends JFrame public class CConn extends CConnection implements UserPasswdGetter, UserMsgBox, OptionsDialogCallback, FdInStreamBlockCallback { + + public final PixelFormat getPreferredPF() { return fullColourPF; } + static final PixelFormat verylowColourPF = + new PixelFormat(8, 3, false, true, 1, 1, 1, 2, 1, 0); + static final PixelFormat lowColourPF = + new PixelFormat(8, 6, false, true, 3, 3, 3, 4, 2, 0); + static final PixelFormat mediumColourPF = + new PixelFormat(8, 8, false, false, 7, 7, 3, 0, 3, 6); + //////////////////////////////////////////////////////////////////// // The following methods are all called from the RFB thread public CConn(VncViewer viewer_, Socket sock_, - String vncServerName, boolean reverse) + String vncServerName) { serverHost = null; serverPort = 0; sock = sock_; viewer = viewer_; + pendingPFChange = false; currentEncoding = Encodings.encodingTight; lastServerEncoding = -1; fullColour = viewer.fullColour.getValue(); lowColourLevel = 2; autoSelect = viewer.autoSelect.getValue(); - shared = viewer.shared.getValue(); formatChange = false; - encodingChange = false; sameMachine = false; + formatChange = false; encodingChange = false; fullScreen = viewer.fullScreen.getValue(); menuKey = Keysyms.F8; options = new OptionsDialog(this); options.initDialog(); clipboardDialog = new ClipboardDialog(this); - firstUpdate = true; pendingUpdate = false; + firstUpdate = true; pendingUpdate = false; continuousUpdates = false; + forceNonincremental = true; supportsSyncFence = false; + - setShared(shared); + setShared(viewer.shared.getValue()); upg = this; msg = this; @@ -212,13 +223,23 @@ public class CConn extends CConnection vlog.info("connected to host "+serverHost+" port "+serverPort); } - sameMachine = sock.sameMachine(); sock.inStream().setBlockCallback(this); setServerName(serverHost); setStreams(sock.inStream(), sock.outStream()); initialiseProtocol(); } + public void refreshFramebuffer() + { + forceNonincremental = true; + + // Without fences, we cannot safely trigger an update request directly + // but must wait for the next update to arrive. + if (supportsSyncFence) + requestNewUpdate(); + } + + public boolean showMsgBox(int flags, String title, String text) { //StringBuffer titleText = new StringBuffer("VNC Viewer: "+title); @@ -308,20 +329,28 @@ public class CConn extends CConnection // If using AutoSelect with old servers, start in FullColor // mode. See comment in autoSelectFormatAndEncoding. - if (cp.beforeVersion(3, 8) && autoSelect) { + if (cp.beforeVersion(3, 8) && autoSelect) fullColour = true; - } serverPF = cp.pf(); + desktop = new DesktopWindow(cp.width, cp.height, serverPF, this); - //desktopEventHandler = desktop.setEventHandler(this); - //desktop.addEventMask(KeyPressMask | KeyReleaseMask); fullColourPF = desktop.getPreferredPF(); - if (!serverPF.trueColour) - fullColour = true; - recreateViewport(); + + // Force a switch to the format and encoding we'd like formatChange = true; encodingChange = true; + + // And kick off the update cycle requestNewUpdate(); + + // This initial update request is a bit of a corner case, so we need + // to help out setting the correct format here. + assert(pendingPFChange); + desktop.setServerPF(pendingPF); + cp.setPF(pendingPF); + pendingPFChange = false; + + recreateViewport(); } // setDesktopSize() is called when the desktop size changes (including when @@ -372,26 +401,34 @@ public class CConn extends CConnection // framebufferUpdateStart() is called at the beginning of an update. // Here we try to send out a new framebuffer update request so that the // next update can be sent out in parallel with us decoding the current - // one. We cannot do this if we're in the middle of a format change - // though. - public void framebufferUpdateStart() { - if (!formatChange) { - pendingUpdate = true; - requestNewUpdate(); - } else - pendingUpdate = false; + // one. + public void framebufferUpdateStart() + { + // Note: This might not be true if sync fences are supported + pendingUpdate = false; + + requestNewUpdate(); } // framebufferUpdateEnd() is called at the end of an update. // For each rectangle, the FdInStream will have timed the speed // of the connection, allowing us to select format and encoding // appropriately, and then request another incremental update. - public void framebufferUpdateEnd() { - desktop.framebufferUpdateEnd(); + public void framebufferUpdateEnd() + { + + desktop.updateWindow(); if (firstUpdate) { int width, height; + // We need fences to make extra update requests and continuous + // updates "safe". See fence() for the next step. + if (cp.supportsFence) + synchronized(this) { + writer().writeFence(fenceTypes.fenceFlagRequest | fenceTypes.fenceFlagSyncNext, 0, null); + } + if (cp.supportsSetDesktopSize && viewer.desktopSize.getValue() != null && viewer.desktopSize.getValue().split("x").length == 2) { @@ -422,16 +459,21 @@ public class CConn extends CConnection screen0.dimensions.br.x = width; screen0.dimensions.br.y = height; - writer().writeSetDesktopSize(width, height, layout); + synchronized(this) { + writer().writeSetDesktopSize(width, height, layout); + } } firstUpdate = false; } - // A format change prevented us from sending this before the update, - // so make sure to send it now. - if (formatChange && !pendingUpdate) - requestNewUpdate(); + // A format change has been scheduled and we are now past the update + // with the old format. Time to active the new one. + if (pendingPFChange) { + desktop.setServerPF(pendingPF); + cp.setPF(pendingPF); + pendingPFChange = false; + } // Compute new settings based on updated bandwidth values if (autoSelect) @@ -480,21 +522,62 @@ public class CConn extends CConnection desktop.copyRect(r.tl.x, r.tl.y, r.width(), r.height(), sx, sy); } - public PixelFormat getPreferredPF() { - return fullColourPF; - } - public void setCursor(int width, int height, Point hotspot, int[] data, byte[] mask) { desktop.setCursor(width, height, hotspot, data, mask); } - private void resizeFramebuffer() + public void fence(int flags, int len, byte[] data) { - if ((cp.width == 0) && (cp.height == 0)) + super.fence(flags, len, data); + + if ((flags & fenceTypes.fenceFlagRequest) != 0) { + // We handle everything synchronously so we trivially honor these modes + flags = flags & (fenceTypes.fenceFlagBlockBefore | fenceTypes.fenceFlagBlockAfter); + + synchronized(this) { + 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; + synchronized(this) { + writer().writeEnableContinuousUpdates(true, 0, 0, cp.width, cp.height); + } + } + } + } else { + // Pixel format change + MemInStream memStream = new MemInStream(data, 0, len); + PixelFormat pf = new PixelFormat(); + + pf.read(memStream); + + desktop.setServerPF(pf); + cp.setPF(pf); + } + } + + private void resizeFramebuffer() + { if (desktop == null) return; + + if (continuousUpdates) + synchronized(this) { + writer().writeEnableContinuousUpdates(true, 0, 0, cp.width, cp.height); + } + + if ((cp.width == 0) && (cp.height == 0)) + return; if ((desktop.width() == cp.width) && (desktop.height() == cp.height)) return; @@ -639,34 +722,63 @@ public class CConn extends CConnection private void requestNewUpdate() { if (formatChange) { + PixelFormat pf; /* Catch incorrect requestNewUpdate calls */ - assert(pendingUpdate == false); + assert(!pendingUpdate || supportsSyncFence); if (fullColour) { - desktop.setPF(fullColourPF); + pf = fullColourPF; } else { if (lowColourLevel == 0) { - desktop.setPF(new PixelFormat(8,3,false,true,1,1,1,2,1,0)); + pf = verylowColourPF; } else if (lowColourLevel == 1) { - desktop.setPF(new PixelFormat(8,6,false,true,3,3,3,4,2,0)); + pf = lowColourPF; } else { - desktop.setPF(new PixelFormat(8,8,false,true,7,7,3,0,3,6)); + pf = mediumColourPF; } } - String str = desktop.getPF().print(); + + if (supportsSyncFence) { + // We let the fence carry the pixel format and switch once we + // get the response back. That way we will be synchronised with + // when the server switches. + MemOutStream memStream = new MemOutStream(); + + pf.write(memStream); + + synchronized(this) { + writer().writeFence(fenceTypes.fenceFlagRequest | fenceTypes.fenceFlagSyncNext, + memStream.length(), (byte[])memStream.data()); + } + } else { + // New requests are sent out at the start of processing the last + // one, so we cannot switch our internal format right now (doing so + // would mean misdecoding the current update). + pendingPFChange = true; + pendingPF = pf; + } + + String str = pf.print(); vlog.info("Using pixel format "+str); - cp.setPF(desktop.getPF()); synchronized (this) { - writer().writeSetPixelFormat(cp.pf()); + writer().writeSetPixelFormat(pf); } + + formatChange = false; } + checkEncodings(); - synchronized (this) { - writer().writeFramebufferUpdateRequest(new Rect(0,0,cp.width,cp.height), - !formatChange); + + if (forceNonincremental || !continuousUpdates) { + pendingUpdate = true; + synchronized (this) { + writer().writeFramebufferUpdateRequest(new Rect(0,0,cp.width,cp.height), + !formatChange); + } } - formatChange = false; + + forceNonincremental = false; } @@ -791,7 +903,7 @@ public class CConn extends CConnection options.secPlain.setEnabled(false); options.sendLocalUsername.setEnabled(false); } else { - options.shared.setSelected(shared); + options.shared.setSelected(viewer.shared.getValue()); /* Process non-VeNCrypt sectypes */ java.util.List<Integer> secTypes = new ArrayList<Integer>(); @@ -971,8 +1083,7 @@ public class CConn extends CConnection menuKey = (options.menuKey.getSelectedIndex()+0xFFBE); F8Menu.f8.setText("Send F"+(menuKey-Keysyms.F1+1)); - shared = options.shared.isSelected(); - setShared(shared); + setShared(options.shared.isSelected()); viewer.useLocalCursor.setParam(options.useLocalCursor.isSelected()); if (cp.supportsLocalCursor != viewer.useLocalCursor.getValue()) { cp.supportsLocalCursor = viewer.useLocalCursor.getValue(); @@ -1226,7 +1337,7 @@ public class CConn extends CConnection } - synchronized public void writeWheelEvent(MouseWheelEvent ev) { + public void writeWheelEvent(MouseWheelEvent ev) { if (state() != RFBSTATE_NORMAL) return; int x, y; int clicks = ev.getWheelRotation(); @@ -1239,9 +1350,11 @@ public class CConn extends CConnection 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); + synchronized(this) { + writer().writePointerEvent(new Point(x, y), buttonMask); + buttonMask = 0; + writer().writePointerEvent(new Point(x, y), buttonMask); + } } writeModifiers(0); @@ -1265,10 +1378,12 @@ public class CConn extends CConnection // The following methods are called from both RFB and GUI threads // checkEncodings() sends a setEncodings message if one is needed. - synchronized private void checkEncodings() { - if (encodingChange && state() == RFBSTATE_NORMAL) { + private void checkEncodings() { + if (encodingChange && (writer() != null)) { vlog.info("Using "+Encodings.encodingName(currentEncoding)+" encoding"); - writer().writeSetEncodings(currentEncoding, true); + synchronized(this) { + writer().writeSetEncodings(currentEncoding, true); + } encodingChange = false; } } @@ -1296,7 +1411,6 @@ public class CConn extends CConnection // reading and writing int and boolean is atomic in java, so no // synchronization of the following flags is needed: - int currentEncoding, lastServerEncoding; int lowColourLevel; @@ -1313,24 +1427,37 @@ public class CConn extends CConnection int buttonMask; int pressedModifiers; - public String serverHost; - public int serverPort; - public Socket sock; + private String serverHost; + private int serverPort; + private Socket sock; + + protected DesktopWindow desktop; + + // FIXME: should be private + public PixelFormat serverPF; + private PixelFormat fullColourPF; + + private boolean pendingPFChange; + private PixelFormat pendingPF; + + private int currentEncoding, lastServerEncoding; + + private boolean formatChange; + private boolean encodingChange; + + private boolean firstUpdate; + private boolean pendingUpdate; + private boolean continuousUpdates; + + private boolean forceNonincremental; + + private boolean supportsSyncFence; + public int menuKey; - PixelFormat serverPF; ViewportFrame viewport; - DesktopWindow desktop; - PixelFormat fullColourPF; - boolean fullColour; - boolean autoSelect; - boolean shared; - boolean formatChange; - boolean encodingChange; - boolean sameMachine; + private boolean fullColour; + private boolean autoSelect; boolean fullScreen; - boolean reverseConnection; - boolean firstUpdate; - boolean pendingUpdate; static LogWriter vlog = new LogWriter("CConn"); } diff --git a/java/com/tigervnc/vncviewer/DesktopWindow.java b/java/com/tigervnc/vncviewer/DesktopWindow.java index 53d2980c..814b9d06 100644 --- a/java/com/tigervnc/vncviewer/DesktopWindow.java +++ b/java/com/tigervnc/vncviewer/DesktopWindow.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2010 D. R. Commander. All Rights Reserved. +/* Copyright (C) 2011-2012 TigerVNC Team. + * Copyright (C) 2010 D. R. Commander. All Rights Reserved. * Copyright (C) 2009 Paul Donohue. All Rights Reserved. * Copyright (C) 2006 Constantin Kaplinsky. All Rights Reserved. * Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. @@ -178,6 +179,10 @@ class DesktopWindow extends JPanel implements return; } + public void setServerPF(PixelFormat pf) { + im.setPF(pf); + } + public PixelFormat getPreferredPF() { return im.getNativePF(); } @@ -202,9 +207,8 @@ class DesktopWindow extends JPanel implements } } -// Update the actual window with the changed parts of the framebuffer. - - public void framebufferUpdateEnd() + // Update the actual window with the changed parts of the framebuffer. + public void updateWindow() { drawInvalidRect(); } @@ -246,19 +250,12 @@ class DesktopWindow extends JPanel implements invalidBottom = y + h; invalidRect = true; } - - if ((invalidRight - invalidLeft) * (invalidBottom - invalidTop) > 100000) - drawInvalidRect(); } public void beginRect(int x, int y, int w, int h, int encoding) { invalidRect = false; } - public void endRect(int x, int y, int w, int h, int encoding) { - drawInvalidRect(); - } - final public void fillRect(int x, int y, int w, int h, int pix) { if (overlapsCursor(x, y, w, h)) hideLocalCursor(); diff --git a/java/com/tigervnc/vncviewer/PixelBufferImage.java b/java/com/tigervnc/vncviewer/PixelBufferImage.java index 9e34c8ab..0efc3c1d 100644 --- a/java/com/tigervnc/vncviewer/PixelBufferImage.java +++ b/java/com/tigervnc/vncviewer/PixelBufferImage.java @@ -1,4 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2011-2012 TigerVNC Team. * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,19 +36,10 @@ public class PixelBufferImage extends PixelBuffer implements ImageProducer cc = cc_; desktop = desktop_; PixelFormat nativePF = getNativePF(); - switch ((nativePF.depth > cc.serverPF.depth) ? cc.serverPF.depth : nativePF.depth) { - case 8: - setPF(new PixelFormat(8,8,false,true,7,7,3,0,3,6)); - break; - case 16: - setPF(new PixelFormat(16,16,false,true,0xF800,0x07C0,0x003E,0,0,0)); - break; - case 24: - setPF(new PixelFormat(32,24,false,true,0xff,0xff,0xff,16,8,0)); - break; - default: - setPF(new PixelFormat(8,8,false,true,7,7,3,0,3,6)); - vlog.debug("Unsupported native PF, defaulting to depth 8"); + if (nativePF.depth > cc.serverPF.depth) { + setPF(cc.serverPF); + } else { + setPF(nativePF); } resize(w, h); } diff --git a/java/com/tigervnc/vncviewer/VncViewer.java b/java/com/tigervnc/vncviewer/VncViewer.java index c080f98c..91504d2c 100644 --- a/java/com/tigervnc/vncviewer/VncViewer.java +++ b/java/com/tigervnc/vncviewer/VncViewer.java @@ -219,7 +219,7 @@ public class VncViewer extends java.applet.Applet implements Runnable } try { - cc = new CConn(this, sock, vncServerName.getValue(), false); + cc = new CConn(this, sock, vncServerName.getValue()); while (true) cc.processMsg(); } catch (EndOfStream e) { |