summaryrefslogtreecommitdiffstats
path: root/java
diff options
context:
space:
mode:
authorBrian Hinz <bphinz@users.sourceforge.net>2012-02-12 20:44:29 +0000
committerBrian Hinz <bphinz@users.sourceforge.net>2012-02-12 20:44:29 +0000
commitaf15db239a665030087fe146b20d4b3213708f3d (patch)
tree2efad69694d73cdb8fc90af86af26ef44e10b084 /java
parent8d37f2054b6da02957b12d02aaf96dd5eecc89c7 (diff)
downloadtigervnc-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.java6
-rw-r--r--java/com/tigervnc/rdr/Exception.java1
-rw-r--r--java/com/tigervnc/rdr/MemInStream.java8
-rw-r--r--java/com/tigervnc/rdr/MemOutStream.java4
-rw-r--r--java/com/tigervnc/rfb/CMsgHandler.java10
-rw-r--r--java/com/tigervnc/rfb/CMsgReaderV3.java29
-rw-r--r--java/com/tigervnc/rfb/CMsgWriter.java7
-rw-r--r--java/com/tigervnc/rfb/CMsgWriterV3.java39
-rw-r--r--java/com/tigervnc/rfb/ConnParams.java44
-rw-r--r--java/com/tigervnc/rfb/Encodings.java14
-rw-r--r--java/com/tigervnc/rfb/JpegCompressor.java48
-rw-r--r--java/com/tigervnc/rfb/MsgTypes.java9
-rw-r--r--java/com/tigervnc/rfb/PixelBuffer.java12
-rw-r--r--java/com/tigervnc/rfb/fenceTypes.java31
-rw-r--r--java/com/tigervnc/vncviewer/CConn.java273
-rw-r--r--java/com/tigervnc/vncviewer/DesktopWindow.java19
-rw-r--r--java/com/tigervnc/vncviewer/PixelBufferImage.java18
-rw-r--r--java/com/tigervnc/vncviewer/VncViewer.java2
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) {