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
diff --git a/java/com/tigervnc/rfb/CMsgHandler.java b/java/com/tigervnc/rfb/CMsgHandler.java
index 3118200..533bfbc 100644
--- a/java/com/tigervnc/rfb/CMsgHandler.java
+++ b/java/com/tigervnc/rfb/CMsgHandler.java
@@ -61,6 +61,16 @@
     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 6d9e254..915b1e0 100644
--- a/java/com/tigervnc/rfb/CMsgReaderV3.java
+++ b/java/com/tigervnc/rfb/CMsgReaderV3.java
@@ -51,6 +51,8 @@
       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 @@
     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 7cafddd..c4f4355 100644
--- a/java/com/tigervnc/rfb/CMsgWriter.java
+++ b/java/com/tigervnc/rfb/CMsgWriter.java
@@ -59,9 +59,16 @@
       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 ec30a82..430c374 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 @@
 	
 	  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 70d6114..7fac126 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 @@
     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 @@
     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 @@
                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 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 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 493d548..4dc129f 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 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 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 0000000..929aa4e
--- /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 a009b39..2d452c8 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 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 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 2712ba9..4371355 100644
--- a/java/com/tigervnc/rfb/PixelBuffer.java
+++ b/java/com/tigervnc/rfb/PixelBuffer.java
@@ -36,9 +36,17 @@
       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 0000000..a8e32d3
--- /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);
+}