Completely reworked Java viewer (contributed by Brian Hinz)


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4413 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/java/src/com/tigervnc/rfb/AliasParameter.java b/java/src/com/tigervnc/rfb/AliasParameter.java
new file mode 100644
index 0000000..2570b87
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/AliasParameter.java
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 AliasParameter extends VoidParameter {
+  public AliasParameter(String name_, String desc_, VoidParameter v) {
+    super(name_, desc_);
+    param = v;
+  }
+
+  public boolean setParam(String v) { return param.setParam(v); }
+  public boolean setParam() { return param.setParam(); }
+
+  public String getDefaultStr() { return param.getDefaultStr(); }
+  public String getValueStr() { return param.getValueStr(); }
+  public boolean isBool() { return param.isBool(); }
+
+  protected VoidParameter param;
+}
diff --git a/java/src/com/tigervnc/rfb/AuthFailureException.java b/java/src/com/tigervnc/rfb/AuthFailureException.java
new file mode 100644
index 0000000..35fabef
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/AuthFailureException.java
@@ -0,0 +1,23 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 AuthFailureException extends Exception {
+  public AuthFailureException(String s) { super(s); }
+}
diff --git a/java/src/com/tigervnc/rfb/BoolParameter.java b/java/src/com/tigervnc/rfb/BoolParameter.java
new file mode 100644
index 0000000..06c6ed7
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/BoolParameter.java
@@ -0,0 +1,51 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 BoolParameter extends VoidParameter {
+  public BoolParameter(String name_, String desc_, boolean v) {
+    super(name_, desc_);
+    value = v;
+    defValue = v;
+  }
+
+  public boolean setParam(String v) {
+    if (v.equals("1") || v.equalsIgnoreCase("on") ||
+        v.equalsIgnoreCase("true") || v.equalsIgnoreCase("yes"))
+      value = true;
+    else if (v.equals("0") || v.equalsIgnoreCase("off") ||
+        v.equalsIgnoreCase("false") || v.equalsIgnoreCase("no"))
+      value = false;
+    else
+      return false;
+    return true;
+  }
+
+  public boolean setParam() { setParam(true); return true; }
+  public void setParam(boolean b) { value = b; }
+
+  public String getDefaultStr() { return defValue ? "1" : "0"; }
+  public String getValueStr() { return value ? "1" : "0"; }
+  public boolean isBool() { return true; }
+
+  final public boolean getValue() { return value; }
+
+  protected boolean value;
+  protected boolean defValue;
+}
diff --git a/java/src/com/tigervnc/rfb/CConnection.java b/java/src/com/tigervnc/rfb/CConnection.java
new file mode 100644
index 0000000..bca2e9d
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/CConnection.java
@@ -0,0 +1,370 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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;
+
+import java.util.*;
+
+import com.tigervnc.rdr.*;
+
+abstract public class CConnection extends CMsgHandler {
+
+  public CConnection() {
+    security = new SecurityClient();
+  }
+
+  // setStreams() sets the streams to be used for the connection.  These must
+  // be set before initialiseProtocol() and processMsg() are called.  The
+  // CSecurity object may call setStreams() again to provide alternative
+  // streams over which the RFB protocol is sent (i.e. encrypting/decrypting
+  // streams).  Ownership of the streams remains with the caller
+  // (i.e. SConnection will not delete them).
+  public void setStreams(InStream is_, OutStream os_) {
+    is = is_;
+    os = os_;
+  }
+
+  // initialiseProtocol() should be called once the streams and security
+  // types are set.  Subsequently, processMsg() should be called whenever
+  // there is data to read on the InStream.
+  public void initialiseProtocol() {
+    state_ = RFBSTATE_PROTOCOL_VERSION;
+  }
+
+  // processMsg() should be called whenever there is data to read on the
+  // InStream.  You must have called initialiseProtocol() first.
+  public void processMsg() {
+    switch (state_) {
+
+    case RFBSTATE_PROTOCOL_VERSION: processVersionMsg();        break;
+    case RFBSTATE_SECURITY_TYPES:   processSecurityTypesMsg();  break;
+    case RFBSTATE_SECURITY:         processSecurityMsg();       break;
+    case RFBSTATE_SECURITY_RESULT:  processSecurityResultMsg(); break;
+    case RFBSTATE_INITIALISATION:   processInitMsg();           break;
+    case RFBSTATE_NORMAL:           reader_.readMsg();          break;
+    case RFBSTATE_UNINITIALISED:
+      throw new Exception("CConnection.processMsg: not initialised yet?");
+    default:
+      throw new Exception("CConnection.processMsg: invalid state");
+    }
+  }
+
+  private void processVersionMsg() {
+    vlog.debug("reading protocol version");
+    boolean done = true;
+    if (!cp.readVersion(is, done)) {
+      state_ = RFBSTATE_INVALID;
+      throw new Exception("reading version failed: not an RFB server?");
+    }
+    if (!done) return;
+
+    vlog.info("Server supports RFB protocol version "+cp.majorVersion+"."+
+              cp.minorVersion);
+
+    // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
+    if (cp.beforeVersion(3,3)) {
+      String msg = ("Server gave unsupported RFB protocol version "+
+                    cp.majorVersion+"."+cp.minorVersion);
+      vlog.error(msg);
+      state_ = RFBSTATE_INVALID;
+      throw new Exception(msg);
+    } else if (useProtocol3_3 || cp.beforeVersion(3,7)) {
+      cp.setVersion(3,3);
+    } else if (cp.afterVersion(3,8)) {
+      cp.setVersion(3,8);
+    }
+
+    cp.writeVersion(os);
+    state_ = RFBSTATE_SECURITY_TYPES;
+
+    vlog.info("Using RFB protocol version "+
+              cp.majorVersion+"."+cp.minorVersion);
+  }
+
+  private void processSecurityTypesMsg() {
+    vlog.info("processing security types message");
+
+    int secType = Security.secTypeInvalid;
+    List<Integer> secTypes = new ArrayList<Integer>();
+    secTypes = security.GetEnabledSecTypes();
+    //for (Iterator i = secTypes.iterator(); i.hasNext(); )
+    //  vlog.info(((Integer)i.next()).toString());
+
+    if (cp.isVersion(3,3)) {
+
+      // legacy 3.3 server may only offer "vnc authentication" or "none"
+
+      secType = is.readU32();
+      if (secType == Security.secTypeInvalid) {
+        throwConnFailedException();
+
+      } else if (secType == Security.secTypeNone || secType == Security.secTypeVncAuth) {
+        Iterator i;
+        for (i = secTypes.iterator(); i.hasNext(); ) {
+          int refType = (Integer)i.next();
+          if (refType == secType) {
+            secType = refType;
+            break;
+          }
+        }
+      
+        if (!i.hasNext())
+          secType = Security.secTypeInvalid;
+      } else {
+        vlog.error("Unknown 3.3 security type "+secType);
+        throw new Exception("Unknown 3.3 security type");
+      }
+
+    } else {
+
+      // 3.7 server will offer us a list
+
+      int nServerSecTypes = is.readU8();
+      if (nServerSecTypes == 0)
+        throwConnFailedException();
+
+      for (int i = 0; i < nServerSecTypes; i++) {
+        int serverSecType = is.readU8();
+        vlog.info("Server offers security type "+
+                   Security.secTypeName(serverSecType)+"("+serverSecType+")");
+
+        /*
+        * Use the first type sent by server which matches client's type.
+        * It means server's order specifies priority.
+        */
+        if (secType == Security.secTypeInvalid) {
+          for (Iterator j = secTypes.iterator(); j.hasNext(); ) {
+            int refType = (Integer)j.next();
+            if (refType == serverSecType) {
+              secType = refType;
+              break;
+            }
+          }
+        }
+      }
+
+      // Inform the server of our decision
+      if (secType != Security.secTypeInvalid) {
+        os.writeU8(secType);
+        os.flush();
+        vlog.info("Choosing security type "+Security.secTypeName(secType)+
+                   "("+secType+")");
+      }
+    }
+
+    if (secType == Security.secTypeInvalid) {
+      state_ = RFBSTATE_INVALID;
+      vlog.error("No matching security types");
+      throw new Exception("No matching security types");
+    }
+
+    state_ = RFBSTATE_SECURITY;
+    csecurity = security.GetCSecurity(secType);
+    processSecurityMsg();
+  }
+
+  private void processSecurityMsg() {
+    vlog.debug("processing security message");
+    if (csecurity.processMsg(this)) {
+      state_ = RFBSTATE_SECURITY_RESULT;
+      processSecurityResultMsg();
+    }
+  }
+
+  private void processSecurityResultMsg() {
+    vlog.debug("processing security result message");
+    int result;
+    if (cp.beforeVersion(3,8) && csecurity.getType() == Security.secTypeNone) {
+      result = Security.secResultOK;
+    } else {
+      if (!is.checkNoWait(1)) return;
+      result = is.readU32();
+    }
+    switch (result) {
+    case Security.secResultOK:
+      securityCompleted();
+      return;
+    case Security.secResultFailed:
+      vlog.debug("auth failed");
+      break;
+    case Security.secResultTooMany:
+      vlog.debug("auth failed - too many tries");
+      break;
+    default:
+      throw new Exception("Unknown security result from server");
+    }
+    String reason;
+    if (cp.beforeVersion(3,8))
+      reason = "Authentication failure";
+    else
+      reason = is.readString();
+    state_ = RFBSTATE_INVALID;
+    throw new AuthFailureException(reason);
+  }
+
+  private void processInitMsg() {
+    vlog.debug("reading server initialisation");
+    reader_.readServerInit();
+  }
+
+  private void throwConnFailedException() {
+    state_ = RFBSTATE_INVALID;
+    String reason;
+    reason = is.readString();
+    throw new ConnFailedException(reason);
+  }
+
+  private void securityCompleted() {
+    state_ = RFBSTATE_INITIALISATION;
+    reader_ = new CMsgReaderV3(this, is);
+    writer_ = new CMsgWriterV3(cp, os);
+    vlog.debug("Authentication success!");
+    authSuccess();
+    writer_.writeClientInit(shared);
+  }
+
+  // Methods to initialise the connection
+
+  // setServerName() is used to provide a unique(ish) name for the server to
+  // which we are connected.  This might be the result of getPeerEndpoint on
+  // a TcpSocket, for example, or a host specified by DNS name & port.
+  // The serverName is used when verifying the Identity of a host (see RA2).
+  public void setServerName(String name) {
+    serverName = name;
+  }
+
+  public void setServerPort(int port) {
+    serverPort = port;
+  }
+ 
+  //public void setEncryptionType(String type) {
+  //  encryptionType = type;
+  //}
+
+  public void initSecTypes() {
+    nSecTypes = 0;
+  }
+
+  // addSecType() should be called once for each security type which the
+  // client supports.  The order in which they're added is such that the
+  // first one is most preferred.
+/*
+  public void addSecType(int secType) {
+    if (nSecTypes == maxSecTypes)
+      throw new Exception("too many security types");
+    secTypes.set(nSecTypes++,secType);
+  }
+*/
+
+  // setShared sets the value of the shared flag which will be sent to the
+  // server upon initialisation.
+  public void setShared(boolean s) { shared = s; }
+
+  // setProtocol3_3 configures whether or not the CConnection should
+  // only ever support protocol version 3.3
+  public void setProtocol3_3(boolean s) { useProtocol3_3 = s; }
+
+  // Methods to be overridden in a derived class
+
+  // getCSecurity() gets the CSecurity object for the given type.  The type
+  // is guaranteed to be one of the secTypes passed in to addSecType().  The
+  // CSecurity object's destroy() method will be called by the CConnection
+  // from its destructor.
+  //abstract public CSecurity getCSecurity(int secType);
+
+  // getCurrentCSecurity() gets the CSecurity instance used for this
+  // connection.
+  //public CSecurity getCurrentCSecurity() { return security; }
+  
+  // setClientSecTypeOrder() determines whether the client should obey the
+  // server's security type preference, by picking the first server security
+  // type that the client supports, or whether it should pick the first type
+  // that the server supports, from the client-supported list of types.
+  public void setClientSecTypeOrder( boolean csto ) {
+    clientSecTypeOrder = csto;
+  }
+
+  // authSuccess() is called when authentication has succeeded.
+  public void authSuccess() {}
+
+  // serverInit() is called when the ServerInit message is received.  The
+  // derived class must call on to CConnection::serverInit().
+  public void serverInit() {
+    state_ = RFBSTATE_NORMAL;
+    vlog.debug("initialisation done");
+  }
+
+  // Other methods
+
+  public CMsgReader reader() { return reader_; }
+  public CMsgWriter writer() { return writer_; }
+
+  public InStream getInStream() { return is; }
+  public OutStream getOutStream() { return os; }
+
+  public String getServerName() { return serverName; }
+  public int getServerPort() { return serverPort; }
+
+  public static final int RFBSTATE_UNINITIALISED = 0;
+  public static final int RFBSTATE_PROTOCOL_VERSION = 1;
+  public static final int RFBSTATE_SECURITY_TYPES = 2;
+  public static final int RFBSTATE_SECURITY = 3;
+  public static final int RFBSTATE_SECURITY_RESULT = 4;
+  public static final int RFBSTATE_INITIALISATION = 5;
+  public static final int RFBSTATE_NORMAL = 6;
+  public static final int RFBSTATE_INVALID = 7;
+
+  public int state() { return state_; }
+
+  protected void setState(int s) { state_ = s; }
+
+  private void throwAuthFailureException() {
+    String reason;
+    vlog.debug("state="+state()+", ver="+cp.majorVersion+"."+cp.minorVersion);
+    if (state() == RFBSTATE_SECURITY_RESULT && !cp.beforeVersion(3,8)) {
+      reason = is.readString();
+    } else {
+      reason = "Authentication failure";
+    }
+    state_ = RFBSTATE_INVALID;
+    vlog.error(reason);
+    throw new AuthFailureException(reason);
+  }
+
+  InStream is = null;
+  OutStream os = null;
+  CMsgReader reader_ = null;
+  CMsgWriter writer_ = null;
+  boolean shared = false;
+  public CSecurity csecurity;
+  public SecurityClient security;
+  public static final int maxSecTypes = 8;
+  int nSecTypes;
+  int[] secTypes;
+  int state_ = RFBSTATE_UNINITIALISED;
+  String serverName;
+  int serverPort;
+  boolean useProtocol3_3 = false;
+  boolean clientSecTypeOrder;
+  public static java.net.Socket sock;
+
+  public static java.net.Socket getSocket() { return sock; }
+  public static void setSocket(java.net.Socket sock_) { sock = sock_; }
+
+  static LogWriter vlog = new LogWriter("CConnection");
+}
diff --git a/java/src/com/tigervnc/rfb/CMsgHandler.java b/java/src/com/tigervnc/rfb/CMsgHandler.java
new file mode 100644
index 0000000..11d2681
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/CMsgHandler.java
@@ -0,0 +1,85 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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.
+ */
+
+//
+// CMsgHandler
+//
+
+package com.tigervnc.rfb;
+
+public class CMsgHandler {
+
+  public CMsgHandler() {
+    cp = new ConnParams();
+  }
+
+  public void setDesktopSize(int width, int height) 
+  {
+    cp.width = width;
+    cp.height = height;
+  }
+
+  public void setExtendedDesktopSize(int reason, int result,
+                                     int width, int height,
+                                     ScreenSet layout) 
+  {
+    cp.supportsSetDesktopSize = true;
+    
+    if ((reason == screenTypes.reasonClient) && (result != screenTypes.resultSuccess))
+      return;
+
+    if (!layout.validate(width, height))
+      vlog.error("Server sent us an invalid screen layout");
+
+    cp.width = width;
+    cp.height = height;
+    cp.screenLayout = layout;
+  }
+
+  public void setPixelFormat(PixelFormat pf) 
+  {
+    cp.setPF(pf);
+  }
+
+  public void setName(String name) 
+  {
+    cp.setName(name);
+  }
+
+  public void setCursor(int width, int height, Point hotspot,
+                        int[] data, byte[] mask) {}
+  public void serverInit() {}
+
+  public void framebufferUpdateStart() {}
+  public void framebufferUpdateEnd() {}
+  public void beginRect(Rect r, int encoding) {}
+  public void endRect(Rect r, int encoding) {}
+
+  public void setColourMapEntries(int firstColour, int nColours, 
+    int[] rgbs) { }
+  public void bell() {}
+  public void serverCutText(String str, int len) {}
+
+  public void fillRect(Rect r, int pix) {}
+  public void imageRect(Rect r, int[] pixels) {}
+  public void copyRect(Rect r, int srcX, int srcY) {}
+
+  public ConnParams cp;
+
+  static LogWriter vlog = new LogWriter("CMsgHandler");
+}
diff --git a/java/src/com/tigervnc/rfb/CMsgReader.java b/java/src/com/tigervnc/rfb/CMsgReader.java
new file mode 100644
index 0000000..66b9d17
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/CMsgReader.java
@@ -0,0 +1,173 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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.
+ */
+
+//
+// CMsgReader - class for reading RFB messages on the client side
+// (i.e. messages from server to client).
+//
+
+package com.tigervnc.rfb;
+
+import com.tigervnc.rdr.*;
+
+abstract public class CMsgReader {
+
+  protected CMsgReader(CMsgHandler handler_, InStream is_) 
+  {
+    imageBufIdealSize = 0;
+    handler = handler_;
+    is = is_;
+    imageBuf = null;
+    imageBufSize = 0;
+    decoders = new Decoder[Encodings.encodingMax+1];
+  }
+
+  protected void readSetColourMapEntries() 
+  {
+    is.skip(1);
+    int firstColour = is.readU16();
+    int nColours = is.readU16();
+    int[] rgbs = new int[nColours * 3];
+    for (int i = 0; i < nColours * 3; i++)
+      rgbs[i] = is.readU16();
+    handler.setColourMapEntries(firstColour, nColours, rgbs);
+  }
+
+  protected void readBell() 
+  {
+    handler.bell();
+  }
+
+  protected void readServerCutText() 
+  {
+    is.skip(3);
+    int len = is.readU32();
+    if (len > 256*1024) {
+      is.skip(len);
+      vlog.error("cut text too long ("+len+" bytes) - ignoring");
+      return;
+    }
+    byte[] buf = new byte[len];
+    is.readBytes(buf, 0, len);
+    handler.serverCutText(new String(buf), len);
+  }
+
+  protected void readFramebufferUpdateStart() 
+  {
+    handler.framebufferUpdateStart();
+  }
+
+  protected void readFramebufferUpdateEnd() 
+  {
+    handler.framebufferUpdateEnd();
+  }
+
+  protected void readRect(Rect r, int encoding) 
+  {
+    if ((r.br.x > handler.cp.width) || (r.br.y > handler.cp.height)) {
+      vlog.error("Rect too big: "+r.width()+"x"+r.height()+" at "+
+                  r.tl.x+","+r.tl.y+" exceeds "+handler.cp.width+"x"+
+                  handler.cp.height);
+      throw new Exception("Rect too big");
+    }
+
+    if (r.is_empty())
+      vlog.error("Ignoring zero size rect");
+
+    handler.beginRect(r, encoding);
+
+    if (encoding == Encodings.encodingCopyRect) {
+      readCopyRect(r);
+    } else {
+
+      if (decoders[encoding] == null) {
+        decoders[encoding] = Decoder.createDecoder(encoding, this);
+        if (decoders[encoding] == null) {
+          vlog.error("Unknown rect encoding "+encoding);
+          throw new Exception("Unknown rect encoding");
+        }
+      }
+      decoders[encoding].readRect(r, handler);
+    }
+
+    handler.endRect(r, encoding);
+  }
+
+  protected void readCopyRect(Rect r) 
+  {
+    int srcX = is.readU16();
+    int srcY = is.readU16();
+    handler.copyRect(r, srcX, srcY);
+  }
+
+  protected void readSetCursor(int width, int height, Point hotspot) 
+  {
+    int data_len = width * height;
+    int mask_len = ((width+7)/8) * height;
+    int[] data = new int[data_len];
+    byte[] mask = new byte[mask_len];
+
+    is.readPixels(data, data_len, (handler.cp.pf().bpp/8), handler.cp.pf().bigEndian);
+    is.readBytes(mask, 0, mask_len);
+
+    handler.setCursor(width, height, hotspot, data, mask);
+  }
+
+  public int[] getImageBuf(int required) { return getImageBuf(required, 0, 0); } 
+
+  public int[] getImageBuf(int required, int requested, int nPixels) 
+  {
+    int requiredBytes = required * (handler.cp.pf().bpp / 8);
+    int requestedBytes = requested * (handler.cp.pf().bpp / 8);
+    int size = requestedBytes;
+    if (size > imageBufIdealSize) size = imageBufIdealSize;
+
+    if (size < requiredBytes)
+      size = requiredBytes;
+
+    if (imageBufSize < size) {
+      imageBufSize = size;
+      imageBuf = new int[imageBufSize];
+    }
+    if (nPixels != 0)
+      nPixels = imageBufSize / (handler.cp.pf().bpp / 8);
+    return imageBuf;
+  }
+
+  public final int bpp() 
+  {
+    return handler.cp.pf().bpp; 
+  }
+
+  abstract public void readServerInit();
+
+  // readMsg() reads a message, calling the handler as appropriate.
+  abstract public void readMsg();
+
+  public InStream getInStream() { return is; }
+
+  public int imageBufIdealSize;
+
+  protected CMsgHandler handler;
+  protected InStream is;
+  protected Decoder[] decoders;
+  protected int[] imageBuf;
+  protected int imageBufSize;
+
+  static LogWriter vlog = new LogWriter("CMsgReader");
+}
diff --git a/java/src/com/tigervnc/rfb/CMsgReaderV3.java b/java/src/com/tigervnc/rfb/CMsgReaderV3.java
new file mode 100644
index 0000000..b865a6c
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/CMsgReaderV3.java
@@ -0,0 +1,139 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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;
+
+import com.tigervnc.rdr.*;
+
+public class CMsgReaderV3 extends CMsgReader {
+
+  public CMsgReaderV3(CMsgHandler handler_, InStream is_) 
+  {
+    super(handler_, is_);
+    nUpdateRectsLeft = 0;
+  }
+
+  public void readServerInit() 
+  {
+    int width = is.readU16();
+    int height = is.readU16();
+    handler.setDesktopSize(width, height);
+    PixelFormat pf = new PixelFormat();
+    pf.read(is);
+    handler.setPixelFormat(pf);
+    String name = is.readString();
+    handler.setName(name);
+    handler.serverInit();
+  }
+
+  public void readMsg() 
+  {
+    if (nUpdateRectsLeft == 0) {
+
+      int type = is.readU8();
+      switch (type) {
+      case MsgTypes.msgTypeFramebufferUpdate:   readFramebufferUpdate(); break;
+      case MsgTypes.msgTypeSetColourMapEntries: readSetColourMapEntries(); break;
+      case MsgTypes.msgTypeBell:                readBell(); break;
+      case MsgTypes.msgTypeServerCutText:       readServerCutText(); break;
+      default:
+        vlog.error("unknown message type "+type);
+        throw new Exception("unknown message type");
+      }
+
+    } else {
+
+      int x = is.readU16();
+      int y = is.readU16();
+      int w = is.readU16();
+      int h = is.readU16();
+      int encoding = is.readU32();
+
+      switch (encoding) {
+      case Encodings.pseudoEncodingDesktopSize:
+        handler.setDesktopSize(w, h);
+        break;
+      case Encodings.pseudoEncodingExtendedDesktopSize:
+        readExtendedDesktopSize(x, y, w, h);
+        break;
+      case Encodings.pseudoEncodingDesktopName:
+        readSetDesktopName(x, y, w, h);
+        break;
+      case Encodings.pseudoEncodingCursor:
+        readSetCursor(w, h, new Point(x,y));
+        break;
+      case Encodings.pseudoEncodingLastRect:
+        nUpdateRectsLeft = 1;     // this rectangle is the last one
+        break;
+      default:
+        readRect(new Rect(x, y, x+w, y+h), encoding);
+        break;
+      }
+
+      nUpdateRectsLeft--;
+      if (nUpdateRectsLeft == 0) handler.framebufferUpdateEnd();
+    }
+  }
+
+  void readFramebufferUpdate() 
+  {
+    is.skip(1);
+    nUpdateRectsLeft = is.readU16();
+    handler.framebufferUpdateStart();
+  }
+
+  void readSetDesktopName(int x, int y, int w, int h)
+  {
+    String name = is.readString();
+  
+    if (x != 0 || y != 0 || w != 0 || h != 0) {
+      vlog.error("Ignoring DesktopName rect with non-zero position/size");
+    } else {
+      handler.setName(name);
+    }
+  
+  }
+  
+  void readExtendedDesktopSize(int x, int y, int w, int h)
+  {
+    int screens, i;
+    int id, flags;
+    int sx, sy, sw, sh;
+    ScreenSet layout = new ScreenSet();
+  
+    screens = is.readU8();
+    is.skip(3);
+  
+    for (i = 0;i < screens;i++) {
+      id = is.readU32();
+      sx = is.readU16();
+      sy = is.readU16();
+      sw = is.readU16();
+      sh = is.readU16();
+      flags = is.readU32();
+  
+      layout.add_screen(new Screen(id, sx, sy, sw, sh, flags));
+    }
+  
+    handler.setExtendedDesktopSize(x, y, w, h, layout);
+  }
+
+  int nUpdateRectsLeft;
+
+  static LogWriter vlog = new LogWriter("CMsgReaderV3");
+}
diff --git a/java/src/com/tigervnc/rfb/CMsgWriter.java b/java/src/com/tigervnc/rfb/CMsgWriter.java
new file mode 100644
index 0000000..79f5ee9
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/CMsgWriter.java
@@ -0,0 +1,164 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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;
+
+import com.tigervnc.rdr.*;
+
+abstract public class CMsgWriter {
+
+  abstract public void writeClientInit(boolean shared);
+
+  public void writeSetPixelFormat(PixelFormat pf) 
+  {
+    startMsg(MsgTypes.msgTypeSetPixelFormat);                                 
+    os.pad(3);
+    pf.write(os);
+    endMsg();
+  }
+
+  public void writeSetEncodings(int nEncodings, int[] encodings) 
+  {
+    startMsg(MsgTypes.msgTypeSetEncodings);
+    os.skip(1);
+    os.writeU16(nEncodings);
+    for (int i = 0; i < nEncodings; i++)
+      os.writeU32(encodings[i]);
+    endMsg();
+  }
+
+  // Ask for encodings based on which decoders are supported.  Assumes higher
+  // encoding numbers are more desirable.
+
+  public void writeSetEncodings(int preferredEncoding, boolean useCopyRect) 
+  {
+    int nEncodings = 0;
+    int[] encodings = new int[Encodings.encodingMax+3];
+    if (cp.supportsLocalCursor)
+      encodings[nEncodings++] = Encodings.pseudoEncodingCursor;
+    if (cp.supportsDesktopResize)
+      encodings[nEncodings++] = Encodings.pseudoEncodingDesktopSize;
+    if (cp.supportsExtendedDesktopSize)
+      encodings[nEncodings++] = Encodings.pseudoEncodingExtendedDesktopSize;
+    if (cp.supportsDesktopRename)
+      encodings[nEncodings++] = Encodings.pseudoEncodingDesktopName;
+    if (Decoder.supported(preferredEncoding)) {
+      encodings[nEncodings++] = preferredEncoding;
+    }
+    if (useCopyRect) {
+      encodings[nEncodings++] = Encodings.encodingCopyRect;
+    }
+
+    /*
+     * Prefer encodings in this order:
+     *
+     *   Tight, ZRLE, Hextile, *
+     */
+
+    if ((preferredEncoding != Encodings.encodingTight) &&
+        Decoder.supported(Encodings.encodingTight))
+      encodings[nEncodings++] = Encodings.encodingTight;
+
+    if ((preferredEncoding != Encodings.encodingZRLE) &&
+        Decoder.supported(Encodings.encodingZRLE))
+      encodings[nEncodings++] = Encodings.encodingZRLE;
+
+    if ((preferredEncoding != Encodings.encodingHextile) &&
+        Decoder.supported(Encodings.encodingHextile))
+      encodings[nEncodings++] = Encodings.encodingHextile;
+
+    // Remaining encodings
+    for (int i = Encodings.encodingMax; i >= 0; i--) {
+      switch (i) {
+      case Encodings.encodingTight:
+      case Encodings.encodingZRLE:
+      case Encodings.encodingHextile:
+        break;
+      default:
+        if ((i != preferredEncoding) && Decoder.supported(i))
+            encodings[nEncodings++] = i;
+      }
+    }
+
+    encodings[nEncodings++] = Encodings.pseudoEncodingLastRect;
+    if (cp.customCompressLevel && cp.compressLevel >= 0 && cp.compressLevel <= 9)
+      encodings[nEncodings++] = Encodings.pseudoEncodingCompressLevel0 + cp.compressLevel;
+    if (!cp.noJpeg && cp.qualityLevel >= 0 && cp.qualityLevel <= 9)
+      encodings[nEncodings++] = Encodings.pseudoEncodingQualityLevel0 + cp.qualityLevel;
+
+    writeSetEncodings(nEncodings, encodings);
+  }
+
+  public void writeFramebufferUpdateRequest(Rect r, boolean incremental) 
+  {
+    startMsg(MsgTypes.msgTypeFramebufferUpdateRequest);
+    os.writeU8(incremental?1:0);
+    os.writeU16(r.tl.x);
+    os.writeU16(r.tl.y);
+    os.writeU16(r.width());
+    os.writeU16(r.height());
+    endMsg();
+  }
+
+  public void writeKeyEvent(int key, boolean down) 
+  {
+    startMsg(MsgTypes.msgTypeKeyEvent);
+    os.writeU8(down?1:0);
+    os.pad(2);
+    os.writeU32(key);
+    endMsg();
+  }
+
+  public void writePointerEvent(Point pos, int buttonMask) 
+  {
+    Point p = new Point(pos.x,pos.y);
+    if (p.x < 0) p.x = 0;
+    if (p.y < 0) p.y = 0;
+    if (p.x >= cp.width) p.x = cp.width - 1;
+    if (p.y >= cp.height) p.y = cp.height - 1;
+
+    startMsg(MsgTypes.msgTypePointerEvent);
+    os.writeU8(buttonMask);
+    os.writeU16(p.x);
+    os.writeU16(p.y);
+    endMsg();
+  }
+
+  public void writeClientCutText(String str, int len) 
+  {
+    startMsg(MsgTypes.msgTypeClientCutText);
+    os.pad(3);
+    os.writeU32(len);
+    os.writeBytes(str.getBytes(), 0, len);
+    endMsg();
+  }
+
+  abstract public void startMsg(int type);
+  abstract public void endMsg();
+
+  public void setOutStream(OutStream os_) { os = os_; }
+
+  ConnParams getConnParams() { return cp; }
+  OutStream getOutStream() { return os; }
+
+  protected CMsgWriter(ConnParams cp_, OutStream os_) {cp = cp_; os = os_;}
+
+  ConnParams cp;
+  OutStream os;
+  static LogWriter vlog = new LogWriter("CMsgWriter");
+}
diff --git a/java/src/com/tigervnc/rfb/CMsgWriterV3.java b/java/src/com/tigervnc/rfb/CMsgWriterV3.java
new file mode 100644
index 0000000..24d4756
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/CMsgWriterV3.java
@@ -0,0 +1,39 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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;
+
+import com.tigervnc.rdr.*;
+
+public class CMsgWriterV3 extends CMsgWriter {
+
+  public CMsgWriterV3(ConnParams cp_, OutStream os_) { super(cp_, os_); }
+
+  public void writeClientInit(boolean shared) {
+    os.writeU8(shared?1:0);
+    endMsg();
+  }
+
+  public void startMsg(int type) {
+    os.writeU8(type);
+  }
+
+  public void endMsg() {
+    os.flush();
+  }
+}
diff --git a/java/src/com/tigervnc/rfb/CSecurity.java b/java/src/com/tigervnc/rfb/CSecurity.java
new file mode 100644
index 0000000..e5b300f
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/CSecurity.java
@@ -0,0 +1,46 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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.
+ */
+
+//
+// CSecurity - class on the client side for handling security handshaking.  A
+// derived class for a particular security type overrides the processMsg()
+// method.  processMsg() is called first when the security type has been
+// decided on, and will keep being called whenever there is data to read from
+// the server until either it returns 0, indicating authentication/security
+// failure, or it returns 1, to indicate success.  A return value of 2
+// (actually anything other than 0 or 1) indicates that it should be called
+// back when there is more data to read.
+//
+// Note that the first time processMsg() is called, there is no guarantee that
+// there is any data to read from the CConnection's InStream, but subsequent
+// calls guarantee there is at least one byte which can be read without
+// blocking.
+
+package com.tigervnc.rfb;
+
+abstract public class CSecurity {
+  abstract public boolean processMsg(CConnection cc);
+  abstract public int getType();
+  abstract public String description();
+  
+  /*
+   * Use variable directly instead of dumb get/set methods.
+   * It MUST be set by viewer.
+   */
+  static UserPasswdGetter upg;
+}
diff --git a/java/src/com/tigervnc/rfb/CSecurityManaged.java b/java/src/com/tigervnc/rfb/CSecurityManaged.java
new file mode 100644
index 0000000..3502289
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/CSecurityManaged.java
@@ -0,0 +1,71 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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;
+
+import java.io.IOException;
+
+import com.tigervnc.rdr.*;
+import com.tigervnc.vncviewer.*;
+
+public class CSecurityManaged extends CSecurity {
+
+  public CSecurityManaged() { }
+
+  public boolean processMsg(CConnection cc) {
+   InStream is = cc.getInStream();
+   OutStream os = cc.getOutStream();
+
+    StringBuffer username = new StringBuffer();
+
+    CConn.upg.getUserPasswd(username, null);
+
+    // Return the response to the server
+    os.writeU8(username.length());
+	  os.writeBytes(username.toString().getBytes(), 0, username.length());
+    os.flush();
+    int serverPort = is.readU16();
+    //if (serverPort==0) { return true; };
+    String serverName = cc.getServerName();
+    vlog.debug("Redirected to "+serverName+" port "+serverPort);
+    try {
+      CConn.getSocket().close();
+      cc.setServerPort(serverPort);
+      sock = new java.net.Socket(serverName, serverPort);
+      sock.setTcpNoDelay(true);
+      sock.setTrafficClass(0x10);
+      CConn.setSocket(sock);
+      vlog.debug("connected to host "+serverName+" port "+serverPort);
+      cc.setStreams(new JavaInStream(sock.getInputStream()),
+                    new JavaOutStream(sock.getOutputStream()));
+      cc.initialiseProtocol();
+    } catch (java.io.IOException e) {
+      e.printStackTrace();
+    }
+    return false;
+  }
+
+  public int getType() { return Security.secTypeManaged; }
+
+  java.net.Socket sock;
+  UserPasswdGetter upg;
+
+  static LogWriter vlog = new LogWriter("Managed");
+  public String description() { return "No Encryption"; }
+
+}
diff --git a/java/src/com/tigervnc/rfb/CSecurityNone.java b/java/src/com/tigervnc/rfb/CSecurityNone.java
new file mode 100644
index 0000000..e31056d
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/CSecurityNone.java
@@ -0,0 +1,27 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 CSecurityNone extends CSecurity {
+
+  public boolean processMsg(CConnection cc) { return true; }
+  public int getType() { return Security.secTypeNone; }
+  public String description() { return "No Encryption"; }
+  static LogWriter vlog = new LogWriter("CSecurityNone");
+}
diff --git a/java/src/com/tigervnc/rfb/CSecurityPlain.java b/java/src/com/tigervnc/rfb/CSecurityPlain.java
new file mode 100644
index 0000000..c790852
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/CSecurityPlain.java
@@ -0,0 +1,50 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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;
+
+import com.tigervnc.rdr.*;
+import com.tigervnc.vncviewer.*;
+
+public class CSecurityPlain extends CSecurity {
+
+  public CSecurityPlain() { }
+
+  public boolean processMsg(CConnection cc) 
+  {
+    OutStream os = cc.getOutStream();
+
+    StringBuffer username = new StringBuffer();
+    StringBuffer password = new StringBuffer();
+
+    CConn.upg.getUserPasswd(username, password);
+
+    // Return the response to the server
+    os.writeU32(username.length());
+    os.writeU32(password.length());
+	  os.writeBytes(username.toString().getBytes(), 0, username.length());
+	  os.writeBytes(password.toString().getBytes(), 0, password.length());
+    os.flush();
+    return true;
+  }
+
+  public int getType() { return Security.secTypePlain; }
+  public String description() { return "ask for username and password"; }
+
+  static LogWriter vlog = new LogWriter("Plain");
+}
diff --git a/java/src/com/tigervnc/rfb/CSecurityStack.java b/java/src/com/tigervnc/rfb/CSecurityStack.java
new file mode 100644
index 0000000..5886268
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/CSecurityStack.java
@@ -0,0 +1,69 @@
+/* Copyright (C) 2005 Martin Koegler
+ * Copyright (C) 2010 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
+ * 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 CSecurityStack extends CSecurity {
+
+  public CSecurityStack(int Type, String Name, CSecurity s0,
+  			       CSecurity s1)
+  {
+    name = Name;
+    type = Type; 
+    state = 0;
+    state0 = s0;
+    state1 = s1;
+  }
+  
+  public boolean processMsg(CConnection cc)
+  {
+    boolean res = true;
+    if (state == 0) {
+      if (state0 != null)
+        res = state0.processMsg(cc);
+  
+      if (!res)
+        return res;
+  
+      state++;
+    }
+  
+    if (state == 1) {
+      if(state1 != null)
+        res = state1.processMsg(cc);
+  
+      if(!res)
+        return res;
+  
+      state++;
+    }
+  
+    return res;
+  }
+
+  public final int getType() { return type; }
+  public final String description() { return name; }
+
+  private int state;
+  private CSecurity state0;
+  private CSecurity state1;
+  private String name;
+  private int type;
+
+}
diff --git a/java/src/com/tigervnc/rfb/CSecurityTLS.java b/java/src/com/tigervnc/rfb/CSecurityTLS.java
new file mode 100644
index 0000000..977987e
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/CSecurityTLS.java
@@ -0,0 +1,251 @@
+/*

+ * Copyright (C) 2003 Sun Microsystems, Inc.

+ *

+ * 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;
+

+import javax.net.ssl.*;

+import java.security.*;

+import java.security.cert.*;

+import java.security.KeyStore;

+import java.io.File;

+import java.io.InputStream;

+import java.io.FileInputStream;

+import java.util.ArrayList;

+import java.util.Collection;

+import javax.swing.JOptionPane;

+

+import com.tigervnc.vncviewer.UserPrefs;

+import com.tigervnc.rdr.*;

+

+public class CSecurityTLS extends CSecurity {

+

+  public static StringParameter x509ca

+  = new StringParameter("x509ca",

+                        "X509 CA certificate", "");

+  public static StringParameter x509crl

+  = new StringParameter("x509crl",

+                        "X509 CRL file", "");

+

+  private void initGlobal() 

+  {

+    try {

+      SSLSocketFactory sslfactory;

+      SSLContext ctx = SSLContext.getInstance("TLS");

+      if (anon) {

+        ctx.init(null, null, null);

+      } else {

+        TrustManager[] myTM = new TrustManager[] { 

+          new MyX509TrustManager() 

+        };

+        ctx.init (null, myTM, null);

+      }

+      sslfactory = ctx.getSocketFactory();

+      try {

+        ssl = (SSLSocket)sslfactory.createSocket(cc.sock,

+						  cc.sock.getInetAddress().getHostName(),

+						  cc.sock.getPort(), true);

+      } catch (java.io.IOException e) { 

+        throw new Exception(e.toString());

+      }

+

+      if (anon) {

+        String[] supported;

+        ArrayList enabled = new ArrayList();

+

+        supported = ssl.getSupportedCipherSuites();

+

+        for (int i = 0; i < supported.length; i++)

+          if (supported[i].matches("TLS_DH_anon.*"))

+	          enabled.add(supported[i]);

+

+        ssl.setEnabledCipherSuites((String[])enabled.toArray(new String[0]));

+      } else {

+        ssl.setEnabledCipherSuites(ssl.getSupportedCipherSuites());

+      }

+

+      ssl.setEnabledProtocols(new String[]{"SSLv3","TLSv1"});

+      ssl.addHandshakeCompletedListener(new MyHandshakeListener());

+    }

+    catch (java.security.GeneralSecurityException e)

+    {

+      vlog.error ("TLS handshake failed " + e.toString ());

+      return;

+    }

+  }

+

+  public CSecurityTLS(boolean _anon) 

+  {

+    anon = _anon;

+    setDefaults();

+    cafile = x509ca.getData(); 

+    crlfile = x509crl.getData(); 

+  }

+

+  public static void setDefaults()

+  {

+    String homeDir = null;

+    

+    if ((homeDir=UserPrefs.getHomeDir()) == null) {

+      vlog.error("Could not obtain VNC home directory path");

+      return;

+    }

+

+    String vnchomedir = homeDir+UserPrefs.getFileSeperator()+".vnc"+

+                        UserPrefs.getFileSeperator();

+    String caDefault = new String(vnchomedir+"x509_ca.pem");

+    String crlDefault = new String(vnchomedir+"x509_crl.pem");

+

+    if (new File(caDefault).exists())

+      x509ca.setDefaultStr(caDefault);

+    if (new File(crlDefault).exists())

+      x509crl.setDefaultStr(crlDefault);

+  }

+

+  public boolean processMsg(CConnection cc) {

+    is = cc.getInStream();

+    os = cc.getOutStream();

+

+    initGlobal();

+

+    if (!is.checkNoWait(1))

+      return false;

+

+    if (is.readU8() == 0) {

+      int result = is.readU32();

+      String reason;

+      if (result == Security.secResultFailed ||

+          result == Security.secResultTooMany)

+        reason = is.readString();

+      else

+        reason = new String("Authentication failure (protocol error)");

+      throw new AuthFailureException(reason);

+    }

+

+    // SSLSocket.getSession blocks until the handshake is complete

+    session = ssl.getSession();

+    if (!session.isValid())

+      throw new Exception("TLS Handshake failed!");

+

+    try {

+      cc.setStreams(new JavaInStream(ssl.getInputStream()),

+		                new JavaOutStream(ssl.getOutputStream()));

+    } catch (java.io.IOException e) { 

+      throw new Exception("Failed to set streams");

+    }

+

+    return true;

+  }

+

+  class MyHandshakeListener implements HandshakeCompletedListener {

+   public void handshakeCompleted(HandshakeCompletedEvent e) {

+     vlog.info("Handshake succesful!");

+     vlog.info("Using cipher suite: " + e.getCipherSuite());

+   }

+  }

+

+  class MyX509TrustManager implements X509TrustManager

+  {

+

+    X509TrustManager tm;

+

+    MyX509TrustManager() throws java.security.GeneralSecurityException

+    {

+      TrustManagerFactory tmf =

+        TrustManagerFactory.getInstance("PKIX");

+      KeyStore ks = KeyStore.getInstance("JKS");

+      CertificateFactory cf = CertificateFactory.getInstance("X.509");

+      try {

+        ks.load(null, null);

+        File cacert = new File(cafile);

+        if (!cacert.exists() || !cacert.canRead())

+          return;

+        InputStream caStream = new FileInputStream(cafile);

+        X509Certificate ca = (X509Certificate)cf.generateCertificate(caStream);

+        ks.setCertificateEntry("CA", ca);

+        PKIXBuilderParameters params = new PKIXBuilderParameters(ks, new X509CertSelector());

+        File crlcert = new File(crlfile);

+        if (!crlcert.exists() || !crlcert.canRead()) {

+          params.setRevocationEnabled(false);

+        } else {

+          InputStream crlStream = new FileInputStream(crlfile);

+          Collection<? extends CRL> crls = cf.generateCRLs(crlStream);

+          CertStoreParameters csp = new CollectionCertStoreParameters(crls);

+          CertStore store = CertStore.getInstance("Collection", csp);

+          params.addCertStore(store);

+          params.setRevocationEnabled(true);

+        }

+        tmf.init(new CertPathTrustManagerParameters(params));

+      } catch (java.io.FileNotFoundException e) { 

+        vlog.error(e.toString());

+      } catch (java.io.IOException e) {

+        vlog.error(e.toString());

+      }

+      tm = (X509TrustManager)tmf.getTrustManagers()[0];

+    }

+

+    public void checkClientTrusted(X509Certificate[] chain, String authType) 

+      throws CertificateException

+    {

+      tm.checkClientTrusted(chain, authType);

+    }

+

+    public void checkServerTrusted(X509Certificate[] chain, String authType)

+      throws CertificateException

+    {

+      try {

+	      tm.checkServerTrusted(chain, authType);

+      } catch (CertificateException e) {

+        Object[] answer = {"Proceed", "Exit"};

+        int ret = JOptionPane.showOptionDialog(null,

+          e.getCause().getLocalizedMessage()+"\n"+

+          "Continue connecting to this host?",

+          "Confirm certificate exception?",

+          JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,

+          null, answer, answer[0]);

+        if (ret == JOptionPane.NO_OPTION)

+          System.exit(1);

+      } catch (java.lang.Exception e) {

+        throw new Exception(e.toString());

+      }

+    }

+

+    public X509Certificate[] getAcceptedIssuers ()

+    {

+      return tm.getAcceptedIssuers();

+    }

+  }

+

+  public final int getType() { return anon ? Security.secTypeTLSNone : Security.secTypeX509None; }

+  public final String description() 

+    { return anon ? "TLS Encryption without VncAuth" : "X509 Encryption without VncAuth"; }

+

+

+  //protected void setParam();

+  //protected void checkSession();

+  protected CConnection cc;

+

+  private boolean anon;

+  private SSLSession session;

+  private String cafile, crlfile;

+  private InStream is;

+  private OutStream os;

+  private SSLSocket ssl;

+

+  static LogWriter vlog = new LogWriter("CSecurityTLS");

+}

diff --git a/java/src/com/tigervnc/rfb/CSecurityVeNCrypt.java b/java/src/com/tigervnc/rfb/CSecurityVeNCrypt.java
new file mode 100644
index 0000000..ae758e7
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/CSecurityVeNCrypt.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2003 Sun Microsystems, Inc.
+ *
+ * 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;
+
+import java.util.*;
+
+import com.tigervnc.rdr.*;
+
+public class CSecurityVeNCrypt extends CSecurity {
+
+  public CSecurityVeNCrypt(SecurityClient sec) 
+  {
+    haveRecvdMajorVersion = false;
+    haveRecvdMinorVersion = false;
+    haveSentVersion = false;
+    haveAgreedVersion = false;
+    haveListOfTypes = false;
+    haveNumberOfTypes = false;
+    haveChosenType = false;
+    majorVersion = 0;
+    minorVersion = 0;
+    chosenType = Security.secTypeVeNCrypt;
+    nAvailableTypes = 0;
+    availableTypes = null;
+    iAvailableType = 0;
+    security = sec;
+  }
+
+  public boolean processMsg(CConnection cc) {
+    InStream is = cc.getInStream();
+    OutStream os = cc.getOutStream();
+
+    /* get major, minor versions, send what we can support (or 0.0 for can't support it) */
+    if (!haveRecvdMinorVersion) {
+      minorVersion = is.readU8();
+      haveRecvdMinorVersion = true;
+      
+      return false;
+    }
+
+    if (!haveRecvdMajorVersion) {
+      majorVersion = is.readU8();
+      haveRecvdMajorVersion = true;
+    }
+
+    /* major version in upper 8 bits and minor version in lower 8 bits */
+    int Version = (majorVersion << 8) | minorVersion;
+
+    if (!haveSentVersion) {
+      /* Currently we don't support former VeNCrypt 0.1 */
+	    if (Version >= 0x0002) {
+        majorVersion = 0;
+        minorVersion = 2;
+	      os.writeU8(majorVersion);
+	      os.writeU8(minorVersion);
+        os.flush();
+	    } else {
+        /* Send 0.0 to indicate no support */
+        majorVersion = 0;
+        minorVersion = 0;
+        os.writeU8(majorVersion);
+        os.writeU8(minorVersion);
+        os.flush();
+	      throw new Exception("Server reported an unsupported VeNCrypt version");
+      }
+    
+      haveSentVersion = true;
+      return false;
+    }
+
+    /* Check that the server is OK */
+    if (!haveAgreedVersion) {
+      if (is.readU8() != 0)
+	      throw new Exception("Server reported it could not support the VeNCrypt version");
+
+      haveAgreedVersion = true;
+      return false;
+    }
+
+    /* get a number of types */
+    if (!haveNumberOfTypes) {
+      nAvailableTypes = is.readU8();
+      iAvailableType = 0;
+  
+      if (nAvailableTypes <= 0)
+	      throw new Exception("The server reported no VeNCrypt sub-types");
+
+      availableTypes = new int[nAvailableTypes];
+      haveNumberOfTypes = true;
+      return false;
+    }
+
+    if (nAvailableTypes > 0) {
+      /* read in the types possible */
+      if (!haveListOfTypes) {
+        if (is.checkNoWait(4)) {
+	        availableTypes[iAvailableType++] = is.readU32();
+	        haveListOfTypes = (iAvailableType >= nAvailableTypes);
+	        vlog.debug("Server offers security type "+
+		        Security.secTypeName(availableTypes[iAvailableType - 1])+" ("+
+		        availableTypes[iAvailableType - 1]+")");
+
+	        if (!haveListOfTypes)
+	          return false;
+
+        } else
+	        return false;
+        }
+
+        /* make a choice and send it to the server, meanwhile set up the stack */
+        if (!haveChosenType) {
+          chosenType = Security.secTypeInvalid;
+          int i;
+          Iterator j;
+          List<Integer> secTypes = new ArrayList<Integer>();
+
+          secTypes = security.GetEnabledExtSecTypes();
+
+          /* Honor server's security type order */
+          for (i = 0; i < nAvailableTypes; i++) {
+            for (j = secTypes.iterator(); j.hasNext(); ) {
+              int refType = (Integer)j.next();
+	            if (refType == availableTypes[i]) {
+	              chosenType = refType;
+	              break;
+	            }
+	          }
+
+	          if (chosenType != Security.secTypeInvalid)
+	            break;
+          }
+
+          vlog.debug("Choosing security type "+Security.secTypeName(chosenType)+
+		                 " ("+chosenType+")");
+
+          /* Set up the stack according to the chosen type: */
+          if (chosenType == Security.secTypeInvalid || chosenType == Security.secTypeVeNCrypt)
+	          throw new AuthFailureException("No valid VeNCrypt sub-type");
+
+          csecurity = security.GetCSecurity(chosenType);
+
+          /* send chosen type to server */
+          os.writeU32(chosenType);
+          os.flush();
+
+          haveChosenType = true;
+      }
+    } else {
+      /*
+       * Server told us that there are 0 types it can support - this should not
+       * happen, since if the server supports 0 sub-types, it doesn't support
+       * this security type
+       */
+      throw new AuthFailureException("The server reported 0 VeNCrypt sub-types");
+    }
+
+    return csecurity.processMsg(cc);
+  }
+
+  public final int getType() { return chosenType; }
+  public final String description() { return Security.secTypeName(chosenType); }
+
+  public static StringParameter secTypesStr;
+
+  private CSecurity csecurity;
+  SecurityClient security;
+  private boolean haveRecvdMajorVersion;
+  private boolean haveRecvdMinorVersion;
+  private boolean haveSentVersion;
+  private boolean haveAgreedVersion;
+  private boolean haveListOfTypes;
+  private boolean haveNumberOfTypes;
+  private boolean haveChosenType;
+  private int majorVersion, minorVersion;
+  private int chosenType;
+  private int nAvailableTypes;
+  private int[] availableTypes;
+  private int iAvailableType;
+  //private final String desc;
+
+  static LogWriter vlog = new LogWriter("CSecurityVeNCrypt");
+}
diff --git a/java/src/com/tigervnc/rfb/CSecurityVncAuth.java b/java/src/com/tigervnc/rfb/CSecurityVncAuth.java
new file mode 100644
index 0000000..f047f53
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/CSecurityVncAuth.java
@@ -0,0 +1,60 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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;
+

+import com.tigervnc.rdr.*;

+import com.tigervnc.vncviewer.*;
+

+public class CSecurityVncAuth extends CSecurity {

+

+  public CSecurityVncAuth() { }

+

+  private static final int vncAuthChallengeSize = 16;

+

+  public boolean processMsg(CConnection cc) 

+  {

+    InStream is = cc.getInStream();

+    OutStream os = cc.getOutStream();

+

+    // Read the challenge & obtain the user's password

+    byte[] challenge = new byte[vncAuthChallengeSize];

+    is.readBytes(challenge, 0, vncAuthChallengeSize);

+    StringBuffer passwd = new StringBuffer();

+    CConn.upg.getUserPasswd(null, passwd);

+

+    // Calculate the correct response

+    byte[] key = new byte[8];

+    int pwdLen = passwd.length();

+    for (int i=0; i<8; i++)

+      key[i] = i<pwdLen ? (byte)passwd.charAt(i) : 0;

+    DesCipher des = new DesCipher(key);

+    for (int j = 0; j < vncAuthChallengeSize; j += 8)

+      des.encrypt(challenge,j,challenge,j);

+

+    // Return the response to the server

+    os.writeBytes(challenge, 0, vncAuthChallengeSize);

+    os.flush();

+    return true;

+  }

+

+  public int getType() { return Security.secTypeVncAuth; }

+  public String description() { return "No Encryption"; }

+

+  static LogWriter vlog = new LogWriter("VncAuth");

+}

diff --git a/java/src/com/tigervnc/rfb/Configuration.java b/java/src/com/tigervnc/rfb/Configuration.java
new file mode 100644
index 0000000..bc67608
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/Configuration.java
@@ -0,0 +1,91 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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.
+ */
+
+//
+// Configuration - class for dealing with configuration parameters.
+//
+
+package com.tigervnc.rfb;
+
+public class Configuration {
+
+  // - Set named parameter to value
+  public static boolean setParam(String name, String value) {
+    VoidParameter param = getParam(name);
+    if (param == null) return false;
+    return param.setParam(value);
+  }
+
+  // - Set parameter to value (separated by "=")
+  public static boolean setParam(String config) {
+    boolean hyphen = false;
+    if (config.charAt(0) == '-') {
+      hyphen = true;
+      if (config.charAt(1) == '-')
+        config = config.substring(2); // allow gnu-style --<option>
+      else
+        config = config.substring(1);
+    }
+    int equal = config.indexOf('=');
+    if (equal != -1) {
+      return setParam(config.substring(0, equal), config.substring(equal+1));
+    } else if (hyphen) {
+      VoidParameter param = getParam(config);
+      if (param == null) return false;
+      return param.setParam();
+    }
+    return false;
+  }
+
+  // - Get named parameter
+  public static VoidParameter getParam(String name) {
+    VoidParameter current = head;
+    while (current != null) {
+      if (name.equalsIgnoreCase(current.getName()))
+        return current;
+      current = current.next;
+    }
+    return null;
+  }
+
+  public static String listParams() {
+    StringBuffer s = new StringBuffer();
+
+    VoidParameter current = head;
+    while (current != null) {
+      String def_str = current.getDefaultStr();
+      String desc = current.getDescription();
+      s.append("  "+current.getName()+" - "+desc+" (default="+def_str+")\n");
+      current = current.next;
+    }
+
+    return s.toString();
+  }
+
+  public static void readAppletParams(java.applet.Applet applet) {
+    VoidParameter current = head;
+    while (current != null) {
+      String str = applet.getParameter(current.getName());
+      if (str != null)
+        current.setParam(str);
+      current = current.next;
+    }
+  }
+
+  public static VoidParameter head;
+}
diff --git a/java/src/com/tigervnc/rfb/ConnFailedException.java b/java/src/com/tigervnc/rfb/ConnFailedException.java
new file mode 100644
index 0000000..d1ddcb4
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/ConnFailedException.java
@@ -0,0 +1,23 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 ConnFailedException extends Exception {
+  public ConnFailedException(String s) { super(s); }
+}
diff --git a/java/src/com/tigervnc/rfb/ConnParams.java b/java/src/com/tigervnc/rfb/ConnParams.java
new file mode 100644
index 0000000..77acea0
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/ConnParams.java
@@ -0,0 +1,172 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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;
+
+import com.tigervnc.rdr.*;
+
+public class ConnParams {
+  static LogWriter vlog = new LogWriter("ConnParams");
+
+  public ConnParams() {
+    majorVersion = 0; minorVersion = 0;
+    width = 0; height = 0; useCopyRect = false;
+    supportsLocalCursor = false; supportsLocalXCursor = false;
+    supportsDesktopResize = false; supportsExtendedDesktopSize = false;
+    supportsDesktopRename = false; supportsLastRect = false;
+    supportsSetDesktopSize = false;
+    customCompressLevel = false; compressLevel = 6;
+    noJpeg = false; qualityLevel = -1; 
+    name_ = null; nEncodings_ = 0; encodings_ = null;
+    currentEncoding_ = Encodings.encodingRaw; verStrPos = 0;
+
+    setName("");
+  }
+
+  public boolean readVersion(InStream is, boolean done) {
+    if (verStrPos >= 12) return false;
+    byte[] verStr = new byte[13];
+    while (verStrPos < 12) {
+      verStr[verStrPos++] = (byte)is.readU8();
+    }
+
+    if (verStrPos < 12) {
+      done = false;
+      return true;
+    }
+    done = true;
+    verStr[12] = 0;
+    majorVersion = (verStr[4] - '0') * 100 + (verStr[5] - '0') * 10 + (verStr[6] - '0');
+    minorVersion = (verStr[8] - '0') * 100 + (verStr[9] - '0') * 10 + (verStr[10] - '0');
+    verStrPos = 0;
+    return true;
+  }
+
+  public void writeVersion(OutStream os) {
+    byte[] b = new byte[12];
+    b[0] = (byte)'R'; b[1] = (byte)'F'; b[2] = (byte)'B'; b[3] = (byte)' ';
+    b[4] = (byte)('0' + (majorVersion / 100) % 10);
+    b[5] = (byte)('0' + (majorVersion / 10) % 10);
+    b[6] = (byte)('0' + majorVersion % 10);
+    b[7] = (byte)'.';
+    b[8] = (byte)('0' + (minorVersion / 100) % 10);
+    b[9] = (byte)('0' + (minorVersion / 10) % 10);
+    b[10] = (byte)('0' + minorVersion % 10);
+    b[11] = (byte)'\n';
+    os.writeBytes(b, 0, 12);
+    os.flush();
+  }
+
+  public int majorVersion;
+  public int minorVersion;
+
+  public void setVersion(int major, int minor) {
+    majorVersion = major; minorVersion = minor;
+  }
+  public boolean isVersion(int major, int minor) {
+    return majorVersion == major && minorVersion == minor;
+  }
+  public boolean beforeVersion(int major, int minor) {
+    return (majorVersion < major ||
+            (majorVersion == major && minorVersion < minor));
+  }
+  public boolean afterVersion(int major, int minor) {
+    return !beforeVersion(major,minor+1);
+  }
+
+  public int width;
+  public int height;
+  public ScreenSet screenLayout;
+
+  public PixelFormat pf() { return pf_; }
+  public void setPF(PixelFormat pf) {
+    pf_ = pf;
+    if (pf.bpp != 8 && pf.bpp != 16 && pf.bpp != 32) {
+      throw new Exception("setPF: not 8, 16 or 32 bpp?");
+    }
+  }
+
+  public String name() { return name_; }
+  public void setName(String name) 
+  {
+    name_ = name;
+  }
+
+  public int currentEncoding() { return currentEncoding_; }
+  public int nEncodings() { return nEncodings_; }
+  public int[] encodings() { return encodings_; }
+  public void setEncodings(int nEncodings, int[] encodings)
+  {
+    if (nEncodings > nEncodings_) {
+      encodings_ = new int[nEncodings];
+    }
+    nEncodings_ = nEncodings;
+    useCopyRect = false;
+    supportsLocalCursor = false;
+    supportsDesktopResize = false;
+    customCompressLevel = false;
+    compressLevel = -1;
+    noJpeg = true;
+    qualityLevel = -1;
+    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.pseudoEncodingDesktopSize)
+        supportsDesktopResize = true;
+      else if (encodings[i] >= Encodings.pseudoEncodingCompressLevel0 &&
+          encodings[i] <= Encodings.pseudoEncodingCompressLevel9) {
+        customCompressLevel = true;
+        compressLevel = encodings[i] - Encodings.pseudoEncodingCompressLevel0;
+      } else if (encodings[i] >= Encodings.pseudoEncodingQualityLevel0 &&
+          encodings[i] <= Encodings.pseudoEncodingQualityLevel9) {
+        noJpeg = false;
+        qualityLevel = encodings[i] - Encodings.pseudoEncodingQualityLevel0;
+      } else if (encodings[i] <= Encodings.encodingMax &&
+               Encoder.supported(encodings[i]))
+        currentEncoding_ = encodings[i];
+    }
+  }
+  public boolean useCopyRect;
+
+  public boolean supportsLocalCursor;
+  public boolean supportsLocalXCursor;
+  public boolean supportsDesktopResize;
+  public boolean supportsExtendedDesktopSize;
+  public boolean supportsDesktopRename;
+  public boolean supportsLastRect;
+
+  public boolean supportsSetDesktopSize;
+
+  public boolean customCompressLevel;
+  public int compressLevel;
+  public boolean noJpeg;
+  public int qualityLevel;
+
+  private PixelFormat pf_;
+  private String name_;
+  private int nEncodings_;
+  private int[] encodings_;
+  private int currentEncoding_;
+  private String verStr;
+  private int verStrPos;
+}
diff --git a/java/src/com/tigervnc/rfb/Cursor.java b/java/src/com/tigervnc/rfb/Cursor.java
new file mode 100644
index 0000000..420eb82
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/Cursor.java
@@ -0,0 +1,34 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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;
+
+import java.awt.*;
+
+public class Cursor extends ManagedPixelBuffer {
+
+  public void setSize(int w, int h) {
+    super.setSize(w, h);
+    if (mask == null || mask.length < maskLen())
+      mask = new byte[maskLen()];
+  }
+  public int maskLen() { return (width() + 7) / 8 * height(); }
+
+  public Point hotspot;
+  public byte[] mask;
+}
diff --git a/java/src/com/tigervnc/rfb/Decoder.java b/java/src/com/tigervnc/rfb/Decoder.java
new file mode 100644
index 0000000..8d42ea5
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/Decoder.java
@@ -0,0 +1,51 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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;
+
+abstract public class Decoder {
+
+  abstract public void readRect(Rect r, CMsgHandler handler);
+
+  static public boolean supported(int encoding) 
+  {
+/*
+    return encoding <= Encodings.encodingMax && createFns[encoding];
+*/
+    return (encoding == Encodings.encodingRaw || 
+            encoding == Encodings.encodingRRE ||
+            encoding == Encodings.encodingHextile || 
+            encoding == Encodings.encodingTight ||
+            encoding == Encodings.encodingZRLE);
+  }
+  static public Decoder createDecoder(int encoding, CMsgReader reader) {
+/*
+    if (encoding <= Encodings.encodingMax && createFns[encoding])
+      return (createFns[encoding])(reader);
+    return 0;
+*/
+    switch(encoding) {
+    case Encodings.encodingRaw:     return new RawDecoder(reader);
+    case Encodings.encodingRRE:     return new RREDecoder(reader);
+    case Encodings.encodingHextile: return new HextileDecoder(reader);
+    case Encodings.encodingTight:   return new TightDecoder(reader);
+    case Encodings.encodingZRLE:    return new ZRLEDecoder(reader);
+    }
+    return null;
+  }
+}
diff --git a/java/src/com/tigervnc/rfb/DesCipher.java b/java/src/com/tigervnc/rfb/DesCipher.java
new file mode 100644
index 0000000..f7ae9db
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/DesCipher.java
@@ -0,0 +1,496 @@
+//
+// This DES class has been extracted from package Acme.Crypto for use in VNC.
+// The bytebit[] array has been reversed so that the most significant bit
+// in each byte of the key is ignored, not the least significant.  Also the
+// unnecessary odd parity code has been removed.
+//
+// These changes are:
+//  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+//
+// 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.
+//
+
+// DesCipher - the DES encryption method
+//
+// The meat of this code is by Dave Zimmerman <dzimm@widget.com>, and is:
+//
+// Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved.
+//
+// Permission to use, copy, modify, and distribute this software
+// and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
+// without fee is hereby granted, provided that this copyright notice is kept 
+// intact. 
+// 
+// WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
+// OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE
+// FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+// DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
+// 
+// THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
+// CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
+// PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
+// NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
+// SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+// SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
+// PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES").  WIDGET WORKSHOP
+// SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
+// HIGH RISK ACTIVITIES.
+//
+//
+// The rest is:
+//
+// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>.  All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+//
+// Visit the ACME Labs Java page for up-to-date versions of this and other
+// fine Java utilities: http://www.acme.com/java/
+
+
+/// The DES encryption method.
+// <P>
+// This is surprisingly fast, for pure Java.  On a SPARC 20, wrapped
+// in Acme.Crypto.EncryptedOutputStream or Acme.Crypto.EncryptedInputStream,
+// it does around 7000 bytes/second.
+// <P>
+// Most of this code is by Dave Zimmerman <dzimm@widget.com>, and is
+// Copyright (c) 1996 Widget Workshop, Inc.  See the source file for details.
+// <P>
+// <A HREF="/resources/classes/Acme/Crypto/DesCipher.java">Fetch the software.</A><BR>
+// <A HREF="/resources/classes/Acme.tar.Z">Fetch the entire Acme package.</A>
+// <P>
+// @see Des3Cipher
+// @see EncryptedOutputStream
+// @see EncryptedInputStream
+
+package com.tigervnc.rfb;
+
+public class DesCipher
+    {
+
+    // Constructor, byte-array key.
+    public DesCipher( byte[] key )
+	{
+	setKey( key );
+	}
+
+    // Key routines.
+
+    private int[] encryptKeys = new int[32];
+    private int[] decryptKeys = new int[32];
+
+    /// Set the key.
+    public void setKey( byte[] key )
+	{
+	deskey( key, true, encryptKeys );
+	deskey( key, false, decryptKeys );
+	}
+
+    // Turn an 8-byte key into internal keys.
+    private void deskey( byte[] keyBlock, boolean encrypting, int[] KnL )
+	{
+	int i, j, l, m, n;
+	int[] pc1m = new int[56];
+	int[] pcr = new int[56];
+	int[] kn = new int[32];
+
+	for ( j = 0; j < 56; ++j )
+	    {
+	    l = pc1[j];
+	    m = l & 07;
+	    pc1m[j] = ( (keyBlock[l >>> 3] & bytebit[m]) != 0 )? 1: 0;
+	    }
+
+	for ( i = 0; i < 16; ++i )
+	    {
+	    if ( encrypting )
+		m = i << 1;
+	    else
+		m = (15-i) << 1;
+	    n = m+1;
+	    kn[m] = kn[n] = 0;
+	    for ( j = 0; j < 28; ++j )
+		{
+		l = j+totrot[i];
+		if ( l < 28 )
+		    pcr[j] = pc1m[l];
+		else
+		    pcr[j] = pc1m[l-28];
+		}
+	    for ( j=28; j < 56; ++j )
+		{
+		l = j+totrot[i];
+		if ( l < 56 )
+		    pcr[j] = pc1m[l];
+		else
+		    pcr[j] = pc1m[l-28];
+		}
+	    for ( j = 0; j < 24; ++j )
+		{
+		if ( pcr[pc2[j]] != 0 )
+		    kn[m] |= bigbyte[j];
+		if ( pcr[pc2[j+24]] != 0 )
+		    kn[n] |= bigbyte[j];
+		}
+	    }
+	cookey( kn, KnL );
+	}
+
+    private void cookey( int[] raw, int KnL[] )
+	{
+	int raw0, raw1;
+	int rawi, KnLi;
+	int i;
+
+	for ( i = 0, rawi = 0, KnLi = 0; i < 16; ++i )
+	    {
+	    raw0 = raw[rawi++];
+	    raw1 = raw[rawi++];
+	    KnL[KnLi]  = (raw0 & 0x00fc0000) <<   6;
+	    KnL[KnLi] |= (raw0 & 0x00000fc0) <<  10;
+	    KnL[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
+	    KnL[KnLi] |= (raw1 & 0x00000fc0) >>>  6;
+	    ++KnLi;
+	    KnL[KnLi]  = (raw0 & 0x0003f000) <<  12;
+	    KnL[KnLi] |= (raw0 & 0x0000003f) <<  16;
+	    KnL[KnLi] |= (raw1 & 0x0003f000) >>>  4;
+	    KnL[KnLi] |= (raw1 & 0x0000003f);
+	    ++KnLi;
+	    }
+	}
+
+
+    // Block encryption routines.
+
+    private int[] tempInts = new int[2];
+
+    /// Encrypt a block of eight bytes.
+    public void encrypt( byte[] clearText, int clearOff, byte[] cipherText, int cipherOff )
+	{
+	squashBytesToInts( clearText, clearOff, tempInts, 0, 2 );
+	des( tempInts, tempInts, encryptKeys );
+	spreadIntsToBytes( tempInts, 0, cipherText, cipherOff, 2 );
+	}
+
+    /// Decrypt a block of eight bytes.
+    public void decrypt( byte[] cipherText, int cipherOff, byte[] clearText, int clearOff )
+	{
+	squashBytesToInts( cipherText, cipherOff, tempInts, 0, 2 );
+	des( tempInts, tempInts, decryptKeys );
+	spreadIntsToBytes( tempInts, 0, clearText, clearOff, 2 );
+	}
+
+    // The DES function.
+    private void des( int[] inInts, int[] outInts, int[] keys )
+	{
+	int fval, work, right, leftt;
+	int round;
+	int keysi = 0;
+
+	leftt = inInts[0];
+	right = inInts[1];
+
+	work   = ((leftt >>>  4) ^ right) & 0x0f0f0f0f;
+	right ^= work;
+	leftt ^= (work << 4);
+
+	work   = ((leftt >>> 16) ^ right) & 0x0000ffff;
+	right ^= work;
+	leftt ^= (work << 16);
+
+	work   = ((right >>>  2) ^ leftt) & 0x33333333;
+	leftt ^= work;
+	right ^= (work << 2);
+
+	work   = ((right >>>  8) ^ leftt) & 0x00ff00ff;
+	leftt ^= work;
+	right ^= (work << 8);
+	right  = (right << 1) | ((right >>> 31) & 1);
+
+	work   = (leftt ^ right) & 0xaaaaaaaa;
+	leftt ^= work;
+	right ^= work;
+	leftt  = (leftt << 1) | ((leftt >>> 31) & 1);
+
+	for ( round = 0; round < 8; ++round )
+	    {
+	    work   = (right << 28) | (right >>> 4);
+	    work  ^= keys[keysi++];
+	    fval   = SP7[ work	       & 0x0000003f ];
+	    fval  |= SP5[(work >>>  8) & 0x0000003f ];
+	    fval  |= SP3[(work >>> 16) & 0x0000003f ];
+	    fval  |= SP1[(work >>> 24) & 0x0000003f ];
+	    work   = right ^ keys[keysi++];
+	    fval  |= SP8[ work         & 0x0000003f ];
+	    fval  |= SP6[(work >>>  8) & 0x0000003f ];
+	    fval  |= SP4[(work >>> 16) & 0x0000003f ];
+	    fval  |= SP2[(work >>> 24) & 0x0000003f ];
+	    leftt ^= fval;
+	    work   = (leftt << 28) | (leftt >>> 4);
+	    work  ^= keys[keysi++];
+	    fval   = SP7[ work	       & 0x0000003f ];
+	    fval  |= SP5[(work >>>  8) & 0x0000003f ];
+	    fval  |= SP3[(work >>> 16) & 0x0000003f ];
+	    fval  |= SP1[(work >>> 24) & 0x0000003f ];
+	    work   = leftt ^ keys[keysi++];
+	    fval  |= SP8[ work	       & 0x0000003f ];
+	    fval  |= SP6[(work >>>  8) & 0x0000003f ];
+	    fval  |= SP4[(work >>> 16) & 0x0000003f ];
+	    fval  |= SP2[(work >>> 24) & 0x0000003f ];
+	    right ^= fval;
+	    }
+
+	right  = (right << 31) | (right >>> 1);
+	work   = (leftt ^ right) & 0xaaaaaaaa;
+	leftt ^= work;
+	right ^= work;
+	leftt  = (leftt << 31) | (leftt >>> 1);
+	work   = ((leftt >>>  8) ^ right) & 0x00ff00ff;
+	right ^= work;
+	leftt ^= (work << 8);
+	work   = ((leftt >>>  2) ^ right) & 0x33333333;
+	right ^= work;
+	leftt ^= (work << 2);
+	work   = ((right >>> 16) ^ leftt) & 0x0000ffff;
+	leftt ^= work;
+	right ^= (work << 16);
+	work   = ((right >>>  4) ^ leftt) & 0x0f0f0f0f;
+	leftt ^= work;
+	right ^= (work << 4);
+	outInts[0] = right;
+	outInts[1] = leftt;
+	}
+
+
+    // Tables, permutations, S-boxes, etc.
+
+    private static byte[] bytebit = {
+	(byte)0x01, (byte)0x02, (byte)0x04, (byte)0x08,
+	(byte)0x10, (byte)0x20, (byte)0x40, (byte)0x80
+	};
+    private static int[] bigbyte = {
+	0x800000, 0x400000, 0x200000, 0x100000,
+	0x080000, 0x040000, 0x020000, 0x010000,
+	0x008000, 0x004000, 0x002000, 0x001000,
+	0x000800, 0x000400, 0x000200, 0x000100,
+	0x000080, 0x000040, 0x000020, 0x000010,
+	0x000008, 0x000004, 0x000002, 0x000001
+	};
+    private static byte[] pc1 = {
+         (byte)56, (byte)48, (byte)40, (byte)32, (byte)24, (byte)16, (byte) 8,
+      (byte) 0, (byte)57, (byte)49, (byte)41, (byte)33, (byte)25, (byte)17,
+	 (byte) 9, (byte) 1, (byte)58, (byte)50, (byte)42, (byte)34, (byte)26,
+      (byte)18, (byte)10, (byte) 2, (byte)59, (byte)51, (byte)43, (byte)35,
+	 (byte)62, (byte)54, (byte)46, (byte)38, (byte)30, (byte)22, (byte)14,
+      (byte) 6, (byte)61, (byte)53, (byte)45, (byte)37, (byte)29, (byte)21,
+	 (byte)13, (byte) 5, (byte)60, (byte)52, (byte)44, (byte)36, (byte)28,
+      (byte)20, (byte)12, (byte) 4, (byte)27, (byte)19, (byte)11, (byte)3
+	};
+    private static int[] totrot = {
+        1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28
+	};
+
+    private static byte[] pc2 = {
+	(byte)13, (byte)16, (byte)10, (byte)23, (byte) 0, (byte) 4,
+	          (byte) 2, (byte)27, (byte)14, (byte) 5, (byte)20, (byte) 9,
+	(byte)22, (byte)18, (byte)11, (byte)3 , (byte)25, (byte) 7,
+	          (byte)15, (byte) 6, (byte)26, (byte)19, (byte)12, (byte) 1,
+	(byte)40, (byte)51, (byte)30, (byte)36, (byte)46, (byte)54,
+	          (byte)29, (byte)39, (byte)50, (byte)44, (byte)32, (byte)47,
+	(byte)43, (byte)48, (byte)38, (byte)55, (byte)33, (byte)52,
+	          (byte)45, (byte)41, (byte)49, (byte)35, (byte)28, (byte)31,
+	};
+
+    private static int[] SP1 = {
+        0x01010400, 0x00000000, 0x00010000, 0x01010404,
+	0x01010004, 0x00010404, 0x00000004, 0x00010000,
+	0x00000400, 0x01010400, 0x01010404, 0x00000400,
+	0x01000404, 0x01010004, 0x01000000, 0x00000004,
+	0x00000404, 0x01000400, 0x01000400, 0x00010400,
+	0x00010400, 0x01010000, 0x01010000, 0x01000404,
+	0x00010004, 0x01000004, 0x01000004, 0x00010004,
+	0x00000000, 0x00000404, 0x00010404, 0x01000000,
+	0x00010000, 0x01010404, 0x00000004, 0x01010000,
+	0x01010400, 0x01000000, 0x01000000, 0x00000400,
+	0x01010004, 0x00010000, 0x00010400, 0x01000004,
+	0x00000400, 0x00000004, 0x01000404, 0x00010404,
+	0x01010404, 0x00010004, 0x01010000, 0x01000404,
+	0x01000004, 0x00000404, 0x00010404, 0x01010400,
+	0x00000404, 0x01000400, 0x01000400, 0x00000000,
+	0x00010004, 0x00010400, 0x00000000, 0x01010004
+	};
+    private static int[] SP2 = {
+	0x80108020, 0x80008000, 0x00008000, 0x00108020,
+	0x00100000, 0x00000020, 0x80100020, 0x80008020,
+	0x80000020, 0x80108020, 0x80108000, 0x80000000,
+	0x80008000, 0x00100000, 0x00000020, 0x80100020,
+	0x00108000, 0x00100020, 0x80008020, 0x00000000,
+	0x80000000, 0x00008000, 0x00108020, 0x80100000,
+	0x00100020, 0x80000020, 0x00000000, 0x00108000,
+	0x00008020, 0x80108000, 0x80100000, 0x00008020,
+	0x00000000, 0x00108020, 0x80100020, 0x00100000,
+	0x80008020, 0x80100000, 0x80108000, 0x00008000,
+	0x80100000, 0x80008000, 0x00000020, 0x80108020,
+	0x00108020, 0x00000020, 0x00008000, 0x80000000,
+	0x00008020, 0x80108000, 0x00100000, 0x80000020,
+	0x00100020, 0x80008020, 0x80000020, 0x00100020,
+	0x00108000, 0x00000000, 0x80008000, 0x00008020,
+	0x80000000, 0x80100020, 0x80108020, 0x00108000
+	};
+    private static int[] SP3 = {
+	0x00000208, 0x08020200, 0x00000000, 0x08020008,
+	0x08000200, 0x00000000, 0x00020208, 0x08000200,
+	0x00020008, 0x08000008, 0x08000008, 0x00020000,
+	0x08020208, 0x00020008, 0x08020000, 0x00000208,
+	0x08000000, 0x00000008, 0x08020200, 0x00000200,
+	0x00020200, 0x08020000, 0x08020008, 0x00020208,
+	0x08000208, 0x00020200, 0x00020000, 0x08000208,
+	0x00000008, 0x08020208, 0x00000200, 0x08000000,
+	0x08020200, 0x08000000, 0x00020008, 0x00000208,
+	0x00020000, 0x08020200, 0x08000200, 0x00000000,
+	0x00000200, 0x00020008, 0x08020208, 0x08000200,
+	0x08000008, 0x00000200, 0x00000000, 0x08020008,
+	0x08000208, 0x00020000, 0x08000000, 0x08020208,
+	0x00000008, 0x00020208, 0x00020200, 0x08000008,
+	0x08020000, 0x08000208, 0x00000208, 0x08020000,
+	0x00020208, 0x00000008, 0x08020008, 0x00020200
+	};
+    private static int[] SP4 = {
+	0x00802001, 0x00002081, 0x00002081, 0x00000080,
+	0x00802080, 0x00800081, 0x00800001, 0x00002001,
+	0x00000000, 0x00802000, 0x00802000, 0x00802081,
+	0x00000081, 0x00000000, 0x00800080, 0x00800001,
+	0x00000001, 0x00002000, 0x00800000, 0x00802001,
+	0x00000080, 0x00800000, 0x00002001, 0x00002080,
+	0x00800081, 0x00000001, 0x00002080, 0x00800080,
+	0x00002000, 0x00802080, 0x00802081, 0x00000081,
+	0x00800080, 0x00800001, 0x00802000, 0x00802081,
+	0x00000081, 0x00000000, 0x00000000, 0x00802000,
+	0x00002080, 0x00800080, 0x00800081, 0x00000001,
+	0x00802001, 0x00002081, 0x00002081, 0x00000080,
+	0x00802081, 0x00000081, 0x00000001, 0x00002000,
+	0x00800001, 0x00002001, 0x00802080, 0x00800081,
+	0x00002001, 0x00002080, 0x00800000, 0x00802001,
+	0x00000080, 0x00800000, 0x00002000, 0x00802080
+	};
+    private static int[] SP5 = {
+	0x00000100, 0x02080100, 0x02080000, 0x42000100,
+	0x00080000, 0x00000100, 0x40000000, 0x02080000,
+	0x40080100, 0x00080000, 0x02000100, 0x40080100,
+	0x42000100, 0x42080000, 0x00080100, 0x40000000,
+	0x02000000, 0x40080000, 0x40080000, 0x00000000,
+	0x40000100, 0x42080100, 0x42080100, 0x02000100,
+	0x42080000, 0x40000100, 0x00000000, 0x42000000,
+	0x02080100, 0x02000000, 0x42000000, 0x00080100,
+	0x00080000, 0x42000100, 0x00000100, 0x02000000,
+	0x40000000, 0x02080000, 0x42000100, 0x40080100,
+	0x02000100, 0x40000000, 0x42080000, 0x02080100,
+	0x40080100, 0x00000100, 0x02000000, 0x42080000,
+	0x42080100, 0x00080100, 0x42000000, 0x42080100,
+	0x02080000, 0x00000000, 0x40080000, 0x42000000,
+	0x00080100, 0x02000100, 0x40000100, 0x00080000,
+	0x00000000, 0x40080000, 0x02080100, 0x40000100
+	};
+    private static int[] SP6 = {
+	0x20000010, 0x20400000, 0x00004000, 0x20404010,
+	0x20400000, 0x00000010, 0x20404010, 0x00400000,
+	0x20004000, 0x00404010, 0x00400000, 0x20000010,
+	0x00400010, 0x20004000, 0x20000000, 0x00004010,
+	0x00000000, 0x00400010, 0x20004010, 0x00004000,
+	0x00404000, 0x20004010, 0x00000010, 0x20400010,
+	0x20400010, 0x00000000, 0x00404010, 0x20404000,
+	0x00004010, 0x00404000, 0x20404000, 0x20000000,
+	0x20004000, 0x00000010, 0x20400010, 0x00404000,
+	0x20404010, 0x00400000, 0x00004010, 0x20000010,
+	0x00400000, 0x20004000, 0x20000000, 0x00004010,
+	0x20000010, 0x20404010, 0x00404000, 0x20400000,
+	0x00404010, 0x20404000, 0x00000000, 0x20400010,
+	0x00000010, 0x00004000, 0x20400000, 0x00404010,
+	0x00004000, 0x00400010, 0x20004010, 0x00000000,
+	0x20404000, 0x20000000, 0x00400010, 0x20004010
+	};
+    private static int[] SP7 = {
+	0x00200000, 0x04200002, 0x04000802, 0x00000000,
+	0x00000800, 0x04000802, 0x00200802, 0x04200800,
+	0x04200802, 0x00200000, 0x00000000, 0x04000002,
+	0x00000002, 0x04000000, 0x04200002, 0x00000802,
+	0x04000800, 0x00200802, 0x00200002, 0x04000800,
+	0x04000002, 0x04200000, 0x04200800, 0x00200002,
+	0x04200000, 0x00000800, 0x00000802, 0x04200802,
+	0x00200800, 0x00000002, 0x04000000, 0x00200800,
+	0x04000000, 0x00200800, 0x00200000, 0x04000802,
+	0x04000802, 0x04200002, 0x04200002, 0x00000002,
+	0x00200002, 0x04000000, 0x04000800, 0x00200000,
+	0x04200800, 0x00000802, 0x00200802, 0x04200800,
+	0x00000802, 0x04000002, 0x04200802, 0x04200000,
+	0x00200800, 0x00000000, 0x00000002, 0x04200802,
+	0x00000000, 0x00200802, 0x04200000, 0x00000800,
+	0x04000002, 0x04000800, 0x00000800, 0x00200002
+	};
+    private static int[] SP8 = {
+	0x10001040, 0x00001000, 0x00040000, 0x10041040,
+	0x10000000, 0x10001040, 0x00000040, 0x10000000,
+	0x00040040, 0x10040000, 0x10041040, 0x00041000,
+	0x10041000, 0x00041040, 0x00001000, 0x00000040,
+	0x10040000, 0x10000040, 0x10001000, 0x00001040,
+	0x00041000, 0x00040040, 0x10040040, 0x10041000,
+	0x00001040, 0x00000000, 0x00000000, 0x10040040,
+	0x10000040, 0x10001000, 0x00041040, 0x00040000,
+	0x00041040, 0x00040000, 0x10041000, 0x00001000,
+	0x00000040, 0x10040040, 0x00001000, 0x00041040,
+	0x10001000, 0x00000040, 0x10000040, 0x10040000,
+	0x10040040, 0x10000000, 0x00040000, 0x10001040,
+	0x00000000, 0x10041040, 0x00040040, 0x10000040,
+	0x10040000, 0x10001000, 0x10001040, 0x00000000,
+	0x10041040, 0x00041000, 0x00041000, 0x00001040,
+	0x00001040, 0x00040040, 0x10000000, 0x10041000
+	};
+
+    // Routines taken from other parts of the Acme utilities.
+
+    /// Squash bytes down to ints.
+    public static void squashBytesToInts( byte[] inBytes, int inOff, int[] outInts, int outOff, int intLen )
+        {
+	for ( int i = 0; i < intLen; ++i )
+	    outInts[outOff + i] = 
+		( ( inBytes[inOff + i * 4    ] & 0xff ) << 24 ) |
+		( ( inBytes[inOff + i * 4 + 1] & 0xff ) << 16 ) |
+		( ( inBytes[inOff + i * 4 + 2] & 0xff ) <<  8 ) |
+		  ( inBytes[inOff + i * 4 + 3] & 0xff );
+        }
+
+    /// Spread ints into bytes.
+    public static void spreadIntsToBytes( int[] inInts, int inOff, byte[] outBytes, int outOff, int intLen )
+        {
+	for ( int i = 0; i < intLen; ++i )
+	    {
+	    outBytes[outOff + i * 4    ] = (byte) ( inInts[inOff + i] >>> 24 );
+	    outBytes[outOff + i * 4 + 1] = (byte) ( inInts[inOff + i] >>> 16 );
+	    outBytes[outOff + i * 4 + 2] = (byte) ( inInts[inOff + i] >>>  8 );
+	    outBytes[outOff + i * 4 + 3] = (byte)   inInts[inOff + i];
+	    }
+        }
+    }
diff --git a/java/src/com/tigervnc/rfb/Encoder.java b/java/src/com/tigervnc/rfb/Encoder.java
new file mode 100644
index 0000000..0964f88
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/Encoder.java
@@ -0,0 +1,25 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 Encoder {
+  static public boolean supported(int encoding) {
+    return false;
+  }
+}
diff --git a/java/src/com/tigervnc/rfb/Encodings.java b/java/src/com/tigervnc/rfb/Encodings.java
new file mode 100644
index 0000000..215178d
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/Encodings.java
@@ -0,0 +1,69 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 Encodings {
+
+  public static final int encodingRaw = 0;
+  public static final int encodingCopyRect = 1;
+  public static final int encodingRRE = 2;
+  public static final int encodingCoRRE = 4;
+  public static final int encodingHextile = 5;
+  public static final int encodingTight = 7;
+  public static final int encodingZRLE = 16;
+
+  public static final int encodingMax = 255;
+
+  public static final int pseudoEncodingXCursor = -240;
+  public static final int pseudoEncodingCursor = -239;
+  public static final int pseudoEncodingDesktopSize = -223;
+  public static final int pseudoEncodingExtendedDesktopSize = -308;
+  public static final int pseudoEncodingDesktopName = -307;
+
+  // TightVNC-specific
+  public static final int pseudoEncodingLastRect = -224;
+  public static final int pseudoEncodingQualityLevel0 = -32;
+  public static final int pseudoEncodingQualityLevel9 = -23;
+  public static final int pseudoEncodingCompressLevel0 = -256;
+  public static final int pseudoEncodingCompressLevel9 = -247;
+
+  public static int encodingNum(String name) {
+    if (name.equalsIgnoreCase("raw"))      return encodingRaw;
+    if (name.equalsIgnoreCase("copyRect")) return encodingCopyRect;
+    if (name.equalsIgnoreCase("RRE"))      return encodingRRE;
+    if (name.equalsIgnoreCase("coRRE"))    return encodingCoRRE;
+    if (name.equalsIgnoreCase("hextile"))  return encodingHextile;
+    if (name.equalsIgnoreCase("Tight"))    return encodingTight;
+    if (name.equalsIgnoreCase("ZRLE"))     return encodingZRLE;
+    return -1;
+  }
+
+  public static String encodingName(int num) {
+    switch (num) {
+    case encodingRaw:           return "raw";
+    case encodingCopyRect:      return "copyRect";
+    case encodingRRE:           return "RRE";
+    case encodingCoRRE:         return "CoRRE";
+    case encodingHextile:       return "hextile";
+    case encodingTight:         return "Tight";
+    case encodingZRLE:          return "ZRLE";
+    default:                    return "[unknown encoding]";
+    }
+  }
+}
diff --git a/java/src/com/tigervnc/rfb/Exception.java b/java/src/com/tigervnc/rfb/Exception.java
new file mode 100644
index 0000000..26ac355
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/Exception.java
@@ -0,0 +1,23 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 Exception extends com.tigervnc.rdr.Exception {
+  public Exception(String s) { super(s); }
+}
diff --git a/java/src/com/tigervnc/rfb/Hextile.java b/java/src/com/tigervnc/rfb/Hextile.java
new file mode 100644
index 0000000..9c05b72
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/Hextile.java
@@ -0,0 +1,27 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 Hextile {
+  public static final int raw = (1 << 0);
+  public static final int bgSpecified = (1 << 1);
+  public static final int fgSpecified = (1 << 2);
+  public static final int anySubrects = (1 << 3);
+  public static final int subrectsColoured = (1 << 4);
+}
diff --git a/java/src/com/tigervnc/rfb/HextileDecoder.java b/java/src/com/tigervnc/rfb/HextileDecoder.java
new file mode 100644
index 0000000..4c32b52
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/HextileDecoder.java
@@ -0,0 +1,101 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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;
+
+import com.tigervnc.rdr.*;
+
+public class HextileDecoder extends Decoder {
+
+  public HextileDecoder(CMsgReader reader_) { reader = reader_; }
+
+  public void readRect(Rect r, CMsgHandler handler) {
+    InStream is = reader.getInStream();
+    int bytesPerPixel = handler.cp.pf().bpp / 8;
+    boolean bigEndian = handler.cp.pf().bigEndian;
+
+    int[] buf = reader.getImageBuf(16 * 16 * 4);
+
+    Rect t = new Rect();
+    int bg = 0;
+    int fg = 0;
+
+    for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
+
+      t.br.y = Math.min(r.br.y, t.tl.y + 16);
+
+      for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) {
+
+        t.br.x = Math.min(r.br.x, t.tl.x + 16);
+
+        int tileType = is.readU8();
+
+        if ((tileType & Hextile.raw) != 0) {
+          is.readPixels(buf, t.area(), bytesPerPixel, bigEndian);
+          handler.imageRect(t, buf);
+          continue;
+        }
+
+        if ((tileType & Hextile.bgSpecified) != 0)
+          bg = is.readPixel(bytesPerPixel, bigEndian);
+
+        int len = t.area();
+        int ptr = 0;
+        while (len-- > 0) buf[ptr++] = bg;
+
+        if ((tileType & Hextile.fgSpecified) != 0)
+          fg = is.readPixel(bytesPerPixel, bigEndian);
+
+        if ((tileType & Hextile.anySubrects) != 0) {
+          int nSubrects = is.readU8();
+
+          for (int i = 0; i < nSubrects; i++) {
+
+            if ((tileType & Hextile.subrectsColoured) != 0)
+              fg = is.readPixel(bytesPerPixel, bigEndian);
+
+            int xy = is.readU8();
+            int wh = is.readU8();
+
+/*
+            Rect s = new Rect();
+            s.tl.x = t.tl.x + ((xy >> 4) & 15);
+            s.tl.y = t.tl.y + (xy & 15);
+            s.br.x = s.tl.x + ((wh >> 4) & 15) + 1;
+            s.br.y = s.tl.y + (wh & 15) + 1;
+*/
+            int x = ((xy >> 4) & 15);
+            int y = (xy & 15);
+            int w = ((wh >> 4) & 15) + 1;
+            int h = (wh & 15) + 1;
+            ptr = y * t.width() + x;
+            int rowAdd = t.width() - w;
+            while (h-- > 0) {
+              len = w;
+              while (len-- > 0) buf[ptr++] = fg;
+              ptr += rowAdd;
+            }
+          }
+        }
+        handler.imageRect(t, buf);
+      }
+    }
+  }
+
+  CMsgReader reader;
+}
diff --git a/java/src/com/tigervnc/rfb/Hostname.java b/java/src/com/tigervnc/rfb/Hostname.java
new file mode 100644
index 0000000..42fda53
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/Hostname.java
@@ -0,0 +1,41 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 Hostname {
+
+  public static String getHost(String vncServerName) {
+    int colonPos = vncServerName.indexOf(':');
+    if (colonPos == 0)
+      return "localhost";
+    if (colonPos == -1)
+      colonPos = vncServerName.length();
+    return vncServerName.substring(0, colonPos);
+  }
+
+  public static int getPort(String vncServerName) {
+    int colonPos = vncServerName.indexOf(':');
+    if (colonPos == -1 || colonPos == vncServerName.length()-1)
+      return 5900;
+    if (vncServerName.charAt(colonPos+1) == ':') {
+      return Integer.parseInt(vncServerName.substring(colonPos+2));
+    }
+    return Integer.parseInt(vncServerName.substring(colonPos+1)) + 5900;
+  }
+}
diff --git a/java/src/com/tigervnc/rfb/IntParameter.java b/java/src/com/tigervnc/rfb/IntParameter.java
new file mode 100644
index 0000000..877063e
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/IntParameter.java
@@ -0,0 +1,44 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 IntParameter extends VoidParameter {
+  public IntParameter(String name_, String desc_, int v) {
+    super(name_, desc_);
+    value = v;
+    defValue = v;
+  }
+
+  public boolean setParam(String v) {
+    try {
+      value = Integer.parseInt(v);
+    } catch (NumberFormatException e) {
+      return false;
+    }
+    return true;
+  }
+
+  public String getDefaultStr() { return Integer.toString(defValue); }
+  public String getValueStr() { return Integer.toString(value); }
+
+  public int getValue() { return value; }
+
+  protected int value;
+  protected int defValue;
+}
diff --git a/java/src/com/tigervnc/rfb/Keysyms.java b/java/src/com/tigervnc/rfb/Keysyms.java
new file mode 100644
index 0000000..6bfafea
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/Keysyms.java
@@ -0,0 +1,88 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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.
+ */
+
+//
+// Keysyms - defines X keysyms for non-character keys.  All keysyms
+// corresponding to characters should be generated by calling
+// UnicodeToKeysym.translate().
+//
+
+package com.tigervnc.rfb;
+
+public class Keysyms {
+
+  public static final int BackSpace = 0xFF08;
+  public static final int Tab = 0xFF09;
+  public static final int Linefeed = 0xFF0A;
+  public static final int Clear = 0xFF0B;
+  public static final int Return = 0xFF0D;
+  public static final int Pause = 0xFF13;
+  public static final int Scroll_Lock = 0xFF14;
+  public static final int Sys_Req = 0xFF15;
+  public static final int Escape = 0xFF1B;
+  public static final int Delete = 0xFFFF;
+
+  public static final int Home = 0xFF50;
+  public static final int Left = 0xFF51;
+  public static final int Up = 0xFF52;
+  public static final int Right = 0xFF53;
+  public static final int Down = 0xFF54;
+  public static final int Prior = 0xFF55;
+  public static final int Page_Up = 0xFF55;
+  public static final int Next = 0xFF56;
+  public static final int Page_Down = 0xFF56;
+  public static final int End = 0xFF57;
+  public static final int Begin = 0xFF58;
+
+  public static final int Select = 0xFF60;
+  public static final int Print = 0xFF61;
+  public static final int Execute = 0xFF62;
+  public static final int Insert = 0xFF63;
+  public static final int Undo = 0xFF65;
+  public static final int Redo = 0xFF66;
+  public static final int Menu = 0xFF67;
+  public static final int Find = 0xFF68;
+  public static final int Cancel = 0xFF69;
+  public static final int Help = 0xFF6A;
+  public static final int Break = 0xFF6B;
+  public static final int Mode_switch = 0xFF7E;
+  public static final int script_switch = 0xFF7E;
+  public static final int Num_Lock = 0xFF7F;
+
+  public static final int F1 = 0xFFBE;
+  public static final int F2 = 0xFFBF;
+  public static final int F3 = 0xFFC0;
+  public static final int F4 = 0xFFC1;
+  public static final int F5 = 0xFFC2;
+  public static final int F6 = 0xFFC3;
+  public static final int F7 = 0xFFC4;
+  public static final int F8 = 0xFFC5;
+  public static final int F9 = 0xFFC6;
+  public static final int F10 = 0xFFC7;
+  public static final int F11 = 0xFFC8;
+  public static final int F12 = 0xFFC9;
+
+  public static final int Shift_L = 0xFFE1;
+  public static final int Shift_R = 0xFFE2;
+  public static final int Control_L = 0xFFE3;
+  public static final int Control_R = 0xFFE4;
+  public static final int Meta_L = 0xFFE7;
+  public static final int Meta_R = 0xFFE8;
+  public static final int Alt_L = 0xFFE9;
+  public static final int Alt_R = 0xFFEA;
+}
diff --git a/java/src/com/tigervnc/rfb/LogWriter.java b/java/src/com/tigervnc/rfb/LogWriter.java
new file mode 100644
index 0000000..c573053
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/LogWriter.java
@@ -0,0 +1,99 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 LogWriter {
+
+  public LogWriter(String name_) {
+    name = name_;
+    level = globalLogLevel;
+    next = log_writers;
+    log_writers = this;
+  }
+
+  public void setLevel(int level_) { level = level_; }
+
+  public void write(int level, String str) {
+    if (level <= this.level) {
+      System.err.println(name+": "+str);
+    }
+  }
+
+  public void error(String str) { write(0, str); }
+  public void status(String str) { write(10, str); }
+  public void info(String str) { write(30, str); }
+  public void debug(String str) { write(100, str); }
+
+  public static boolean setLogParams(String params) {
+    globalLogLevel = Integer.parseInt(params);
+    LogWriter current = log_writers;
+    while (current != null) {
+      current.setLevel(globalLogLevel);
+      current = current.next;
+    }
+    return true;
+//      int colon = params.indexOf(':');
+//      String logwriter_name = params.substring(0, colon);
+//      params = params.substring(colon+1);
+//      colon = params.indexOf(':');
+//      String logger_name = params.substring(0, colon);
+//      params = params.substring(colon+1);
+//      int level = Integer.parseInt(params);
+//      // XXX ignore logger name for the moment
+
+//      System.err.println("setting level to "+level);
+//      System.err.println("logwriters is "+log_writers);
+//      if (logwriter_name.equals("*")) {
+//        LogWriter current = log_writers;
+//        while (current != null) {
+//          //current.setLog(logger);
+//          System.err.println("setting level of "+current.name+" to "+level);
+//          current.setLevel(level);
+//          current = current.next;
+//        }
+//        return true;
+//      }
+
+//      LogWriter logwriter = getLogWriter(logwriter_name);
+//      if (logwriter == null) {
+//        System.err.println("no logwriter found: "+logwriter_name);
+//        return false;
+//      }
+
+//      //logwriter.setLog(logger);
+//      logwriter.setLevel(level);
+//      return true;
+  }
+
+
+  static LogWriter getLogWriter(String name) {
+    LogWriter current = log_writers;
+    while (current != null) {
+      if (name.equalsIgnoreCase(current.name)) return current;
+      current = current.next;
+    }
+    return null;
+  }
+
+  String name;
+  int level;
+  LogWriter next;
+  static LogWriter log_writers;
+  static int globalLogLevel = 30;
+}
diff --git a/java/src/com/tigervnc/rfb/ManagedPixelBuffer.java b/java/src/com/tigervnc/rfb/ManagedPixelBuffer.java
new file mode 100644
index 0000000..46b5acf
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/ManagedPixelBuffer.java
@@ -0,0 +1,38 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 ManagedPixelBuffer extends PixelBuffer {
+  public void setSize(int w, int h) {
+    width_ = w;
+    height_ = h;
+    checkDataSize();
+  }
+  public void setPF(PixelFormat pf) {
+    super.setPF(pf);
+    checkDataSize();
+  }
+
+  public int dataLen() { return area(); }
+
+  final void checkDataSize() {
+    if (data == null || data.length < dataLen())
+      data = new int[dataLen()];
+  }
+}
diff --git a/java/src/com/tigervnc/rfb/MsgTypes.java b/java/src/com/tigervnc/rfb/MsgTypes.java
new file mode 100644
index 0000000..a009b39
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/MsgTypes.java
@@ -0,0 +1,40 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 MsgTypes {
+  // server to client
+
+  public static final int msgTypeFramebufferUpdate = 0;
+  public static final int msgTypeSetColourMapEntries = 1;
+  public static final int msgTypeBell = 2;
+  public static final int msgTypeServerCutText = 3;
+
+  // client to server
+
+  public static final int msgTypeSetPixelFormat = 0;
+  public static final int msgTypeFixColourMapEntries = 1;
+  public static final int msgTypeSetEncodings = 2;
+  public static final int msgTypeFramebufferUpdateRequest = 3;
+  public static final int msgTypeKeyEvent = 4;
+  public static final int msgTypePointerEvent = 5;
+  public static final int msgTypeClientCutText = 6;
+
+  public static final int msgTypeSetDesktopSize = 251;
+}
diff --git a/java/src/com/tigervnc/rfb/PixelBuffer.java b/java/src/com/tigervnc/rfb/PixelBuffer.java
new file mode 100644
index 0000000..f87fead
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/PixelBuffer.java
@@ -0,0 +1,116 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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.
+ */
+
+//
+// PixelBuffer - note that this code is only written for the 8, 16, and 32 bpp cases at the
+// moment.
+//
+
+package com.tigervnc.rfb;
+
+import java.awt.image.*;
+
+public class PixelBuffer {
+
+  public PixelBuffer() {
+    setPF(new PixelFormat());
+  }
+
+  public void setPF(PixelFormat pf) {
+    if (!(pf.bpp == 32) && !(pf.bpp == 16) && !(pf.bpp == 8))
+      throw new Exception("Internal error: bpp must be 8, 16, or 32 in PixelBuffer ("+pf.bpp+")");
+    format = pf;
+    switch (pf.depth) {
+    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));
+      break;
+    case 16: 
+      cm = new DirectColorModel(32, 0xF800, 0x07C0, 0x003E, (0xff << 24));
+      break;
+    case 24: 
+      cm = new DirectColorModel(32, (0xff << 16), (0xff << 8), 0xff, (0xff << 24));
+      break;
+    }
+  }
+  public PixelFormat getPF() { return format; }
+
+  public final int width() { return width_; }
+  public final int height() { return height_; }
+  public final int area() { return width_ * height_; }
+
+  public void fillRect(int x, int y, int w, int h, int pix) {
+    for (int ry = y; ry < y + h; ry++)
+      for (int rx = x; rx < x + w; rx++)
+        data[ry * width_ + rx] = pix;
+  }
+
+  public void imageRect(int x, int y, int w, int h, int[] pix) {
+    for (int j = 0; j < h; j++)
+      System.arraycopy(pix, (w * j), data, width_ * (y + j) + x, w);
+  }
+
+  public void copyRect(int x, int y, int w, int h, int srcX, int srcY) {
+    int dest = (width_ * y) + x;
+    int src = (width_ * srcY) + srcX;
+    int inc = width_;
+
+    if (y > srcY) {
+      src += (h-1) * inc;
+      dest += (h-1) * inc;
+      inc = -inc;
+    }
+    int destEnd = dest + h * inc;
+
+    while (dest != destEnd) {
+      System.arraycopy(data, src, data, dest, w);
+      src += inc;
+      dest += inc;
+    }
+  }
+
+  public void maskRect(int x, int y, int w, int h, int[] pix, byte[] mask) {
+    int maskBytesPerRow = (w + 7) / 8;
+    
+    for (int j = 0; j < h; j++) {
+      int cy = y + j;
+      
+      if (cy < 0 || cy >= height_)
+        continue;
+
+      for (int i = 0; i < w; i++) {
+        int cx = x + i;
+
+        if (cx < 0 || cx >= width_)
+          continue;
+
+        int byte_ = j * maskBytesPerRow + i / 8;
+        int bit = 7 - i % 8;
+
+        if ((mask[byte_] & (1 << bit)) != 0)
+         data[cy * width_ + cx] = pix[j * w + i];
+      }     
+    }
+  }
+
+  public int[] data;
+  public ColorModel cm;
+
+  protected PixelFormat format;
+  protected int width_, height_;
+}
diff --git a/java/src/com/tigervnc/rfb/PixelFormat.java b/java/src/com/tigervnc/rfb/PixelFormat.java
new file mode 100644
index 0000000..a8ab5f1
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/PixelFormat.java
@@ -0,0 +1,163 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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.
+ */
+
+//
+// PixelFormat
+//
+
+package com.tigervnc.rfb;
+
+import com.tigervnc.rdr.*;
+
+public class PixelFormat {
+
+  public PixelFormat(int b, int d, boolean e, boolean t) {
+    bpp = b;
+    depth = d;
+    bigEndian = e;
+    trueColour = t;
+  }
+  public PixelFormat(int b, int d, boolean e, boolean t,
+                     int rm, int gm, int bm, int rs, int gs, int bs) {
+    this(b, d, e, t);
+    redMax = rm;
+    greenMax = gm;
+    blueMax = bm;
+    redShift = rs;
+    greenShift = gs;
+    blueShift = bs;
+  }
+  public PixelFormat() { this(8,8,false,true,7,7,3,0,3,6); }
+
+  public boolean equal(PixelFormat x) {
+    return (bpp == x.bpp &&
+            depth == x.depth &&
+            (bigEndian == x.bigEndian || bpp == 8) &&
+            trueColour == x.trueColour &&
+            (!trueColour || (redMax == x.redMax &&
+                             greenMax == x.greenMax &&
+                             blueMax == x.blueMax &&
+                             redShift == x.redShift &&
+                             greenShift == x.greenShift &&
+                             blueShift == x.blueShift)));
+  }
+
+  public void read(InStream is) {
+    bpp = is.readU8();
+    depth = is.readU8();
+    bigEndian = is.readU8()!=0;
+    trueColour = is.readU8()!=0;
+    redMax = is.readU16();
+    greenMax = is.readU16();
+    blueMax = is.readU16();
+    redShift = is.readU8();
+    greenShift = is.readU8();
+    blueShift = is.readU8();
+    is.skip(3);
+  }
+
+  public void write(OutStream os) {
+    os.writeU8(bpp);
+    os.writeU8(depth);
+    os.writeU8(bigEndian?1:0);
+    os.writeU8(trueColour?1:0);
+    os.writeU16(redMax);
+    os.writeU16(greenMax);
+    os.writeU16(blueMax);
+    os.writeU8(redShift);
+    os.writeU8(greenShift);
+    os.writeU8(blueShift);
+    os.pad(3);
+  }
+
+  public final boolean is888() {
+    if(!trueColour)
+      return false;
+    if(bpp != 32)
+      return false;
+    if(depth != 24)
+      return false;
+    if(redMax != 255)
+      return false;
+    if(greenMax != 255)
+      return false;
+    if(blueMax != 255)
+      return false;
+
+    return true;
+  }
+
+  public void bufferFromRGB(int dst, byte[] src) {
+    if (bigEndian) {
+      dst =
+        (src[0] & 0xFF) << 16 | (src[1] & 0xFF) << 8 | (src[2] & 0xFF) | 0xFF << 24;
+    } else {
+      dst =
+        (src[2] & 0xFF) << 16 | (src[1] & 0xFF) << 8 | (src[0] & 0xFF) | 0xFF << 24;
+    }
+  }
+
+  public String print() {
+    StringBuffer s = new StringBuffer();
+    s.append("depth "+depth+" ("+bpp+"bpp)");
+    if (bpp != 8) {
+      if (bigEndian)
+        s.append(" big-endian");
+      else
+        s.append(" little-endian");
+    }
+
+    if (!trueColour) {
+      s.append(" colour-map");
+      return s.toString();
+    }
+
+    if (blueShift == 0 && greenShift > blueShift && redShift > greenShift &&
+        blueMax  == (1 << greenShift) - 1 &&
+        greenMax == (1 << (redShift-greenShift)) - 1 &&
+        redMax   == (1 << (depth-redShift)) - 1)
+    {
+      s.append(" rgb"+(depth-redShift)+(redShift-greenShift)+greenShift);
+      return s.toString();
+    }
+
+    if (redShift == 0 && greenShift > redShift && blueShift > greenShift &&
+        redMax   == (1 << greenShift) - 1 &&
+        greenMax == (1 << (blueShift-greenShift)) - 1 &&
+        blueMax  == (1 << (depth-blueShift)) - 1)
+    {
+      s.append(" bgr"+(depth-blueShift)+(blueShift-greenShift)+greenShift);
+      return s.toString();
+    }
+
+    s.append(" rgb max "+redMax+","+greenMax+","+blueMax+" shift "+redShift+
+             ","+greenShift+","+blueShift);
+    return s.toString();
+  }
+
+  public int bpp;
+  public int depth;
+  public boolean bigEndian;
+  public boolean trueColour;
+  public int redMax;
+  public int greenMax;
+  public int blueMax;
+  public int redShift;
+  public int greenShift;
+  public int blueShift;
+}
diff --git a/java/src/com/tigervnc/rfb/Point.java b/java/src/com/tigervnc/rfb/Point.java
new file mode 100644
index 0000000..25de8c2
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/Point.java
@@ -0,0 +1,40 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 Point {
+
+  // Point
+  //
+  // Represents a point in 2D space, by X and Y coordinates.
+  // Can also be used to represent a delta, or offset, between
+  // two Points.
+  // Functions are provided to allow Points to be compared for
+  // equality and translated by a supplied offset.
+  // Functions are also provided to negate offset Points.
+
+  public Point() {x=0; y=0;}
+  public Point(int x_, int y_) { x=x_; y=y_;}
+  public final Point negate() {return new Point(-x, -y);}
+  public final boolean equals(Point p) {return (x==p.x && y==p.y);}
+  public final Point translate(Point p) {return new Point(x+p.x, y+p.y);}
+  public final Point subtract(Point p) {return new Point(x-p.x, y-p.y);}
+  public int x, y;
+
+}
diff --git a/java/src/com/tigervnc/rfb/RREDecoder.java b/java/src/com/tigervnc/rfb/RREDecoder.java
new file mode 100644
index 0000000..e0ff5cf
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/RREDecoder.java
@@ -0,0 +1,46 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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;
+
+import com.tigervnc.rdr.*;
+
+public class RREDecoder extends Decoder {
+
+  public RREDecoder(CMsgReader reader_) { reader = reader_; }
+
+  public void readRect(Rect r, CMsgHandler handler) {
+    InStream is = reader.getInStream();
+    int bytesPerPixel = handler.cp.pf().bpp / 8;
+    boolean bigEndian = handler.cp.pf().bigEndian;
+    int nSubrects = is.readU32();
+    int bg = is.readPixel(bytesPerPixel, bigEndian);
+    handler.fillRect(r, bg);
+
+    for (int i = 0; i < nSubrects; i++) {
+      int pix = is.readPixel(bytesPerPixel, bigEndian);
+      int x = is.readU16();
+      int y = is.readU16();
+      int w = is.readU16();
+      int h = is.readU16();
+      handler.fillRect(new Rect(r.tl.x+x, r.tl.y+y, r.tl.x+x+w, r.tl.y+y+h), pix);
+    }
+  }
+
+  CMsgReader reader;
+}
diff --git a/java/src/com/tigervnc/rfb/RawDecoder.java b/java/src/com/tigervnc/rfb/RawDecoder.java
new file mode 100644
index 0000000..79db826
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/RawDecoder.java
@@ -0,0 +1,45 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 RawDecoder extends Decoder {
+
+  public RawDecoder(CMsgReader reader_) { reader = reader_; }
+
+  public void readRect(Rect r, CMsgHandler handler) {
+    int x = r.tl.x;
+    int y = r.tl.y;
+    int w = r.width();
+    int h = r.height();
+    int[] imageBuf = reader.getImageBuf(w * h);
+    int nPixels = imageBuf.length / (reader.bpp() / 8);
+    int bytesPerRow = w * (reader.bpp() / 8);
+    while (h > 0) {
+      int nRows = nPixels / w;
+      if (nRows > h) nRows = h;
+      reader.is.readPixels(imageBuf, w * h, (reader.bpp() / 8), handler.cp.pf().bigEndian);
+      handler.imageRect(new Rect(x, y, x+w, y+nRows), imageBuf);
+      h -= nRows;
+      y += nRows;
+    }
+  }
+
+  CMsgReader reader;
+  static LogWriter vlog = new LogWriter("RawDecoder");
+}
diff --git a/java/src/com/tigervnc/rfb/Rect.java b/java/src/com/tigervnc/rfb/Rect.java
new file mode 100644
index 0000000..fab4f5d
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/Rect.java
@@ -0,0 +1,89 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 Rect {
+
+  // Rect
+  //
+  // Represents a rectangular region defined by its top-left (tl)
+  // and bottom-right (br) Points.
+  // Rects may be compared for equality, checked to determine whether
+  // or not they are empty, cleared (made empty), or intersected with
+  // one another.  The bounding rectangle of two existing Rects
+  // may be calculated, as may the area of a Rect.
+  // Rects may also be translated, in the same way as Points, by
+  // an offset specified in a Point structure.
+
+  public Rect() {
+    tl=new Point(0,0);
+    br=new Point(0,0);
+  }
+  public Rect(Point tl_, Point br_) { 
+    tl=new Point(tl_.x, tl_.y);
+    br=new Point(br_.x,br_.y);
+  }
+  public Rect(int x1, int y1, int x2, int y2) {
+    tl=new Point(x1, y1); 
+    br=new Point(x2, y2);
+  }
+  public final void setXYWH(int x, int y, int w, int h) {
+    tl.x = x; tl.y = y; br.x = x+w; br.y = y+h;
+  }
+  public final Rect intersect(Rect r) {
+    Rect result = new Rect();
+    result.tl.x = Math.max(tl.x, r.tl.x);
+    result.tl.y = Math.max(tl.y, r.tl.y);
+    result.br.x = Math.max(Math.min(br.x, r.br.x), result.tl.x);
+    result.br.y = Math.max(Math.min(br.y, r.br.y), result.tl.y);
+    return result;
+  }
+  public final Rect union_boundary(Rect r) {
+    if (r.is_empty()) return this;
+    if (is_empty()) return r;
+    Rect result = new Rect();
+    result.tl.x = Math.min(tl.x, r.tl.x);
+    result.tl.y = Math.min(tl.y, r.tl.y);
+    result.br.x = Math.max(br.x, r.br.x);
+    result.br.y = Math.max(br.y, r.br.y);
+    return result;
+  }
+  public final Rect translate(Point p) {
+    return new Rect(tl.translate(p), br.translate(p));
+  }
+  public final boolean equals(Rect r) {return r.tl.equals(tl) && r.br.equals(br);}
+  public final boolean is_empty() {return (tl.x >= br.x) || (tl.y >= br.y);}
+  public final void clear() {tl = new Point(); br = new Point();}
+  public final boolean enclosed_by(Rect r) {
+    return (tl.x>=r.tl.x) && (tl.y>=r.tl.y) && (br.x<=r.br.x) && (br.y<=r.br.y);
+  }
+  public final boolean overlaps(Rect r) {
+    return tl.x < r.br.x && tl.y < r.br.y && br.x > r.tl.x && br.y > r.tl.y;
+  }
+  public final int area() {return is_empty() ? 0 : (br.x-tl.x)*(br.y-tl.y);}
+  public final Point dimensions() {return new Point(width(), height());}
+  public final int width() {return br.x-tl.x;}
+  public final int height() {return br.y-tl.y;}
+  public final boolean contains(Point p) {
+    return (tl.x<=p.x) && (tl.y<=p.y) && (br.x>p.x) && (br.y>p.y);
+  }
+  public Point tl;
+  public Point br;
+
+}
diff --git a/java/src/com/tigervnc/rfb/Screen.java b/java/src/com/tigervnc/rfb/Screen.java
new file mode 100644
index 0000000..90b22b6
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/Screen.java
@@ -0,0 +1,48 @@
+/* Copyright 2009 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.
+ */
+
+// Represents a single RFB virtual screen, which includes
+// coordinates, an id and flags.
+
+package com.tigervnc.rfb;
+
+public class Screen {
+
+    public Screen() { id=0; flags=0; dimensions = new Rect(); }
+
+    public Screen(int id_, int x_, int y_, int w_, int h_, int flags_) {
+      id = id_;
+      dimensions = new Rect(x_, y_, x_+w_, y_+h_);
+      flags = flags_;
+    }
+
+    public final static boolean operator(Screen r) {
+      if (id != r.id)
+        return false;
+      if (!dimensions.equals(r.dimensions))
+        return false;
+      if (flags != r.flags)
+        return false;
+      return true;
+    }
+
+    public static int id;
+    public static Rect dimensions;
+    public static int flags;
+
+}
diff --git a/java/src/com/tigervnc/rfb/ScreenSet.java b/java/src/com/tigervnc/rfb/ScreenSet.java
new file mode 100644
index 0000000..071282f
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/ScreenSet.java
@@ -0,0 +1,89 @@
+/* Copyright 2009 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.
+ */
+
+// Management class for the RFB virtual screens
+
+package com.tigervnc.rfb;
+
+import java.util.*;
+
+public class ScreenSet {
+
+  // Represents a complete screen configuration, excluding framebuffer
+  // dimensions.
+
+  public ScreenSet() {
+    screens = new ArrayList<Screen>();
+  }
+
+  public static final int num_screens() { return screens.size(); }
+
+  public static final void add_screen(Screen screen) { screens.add(screen); }
+  public static final void remove_screen(int id) { 
+    for (Iterator iter = screens.iterator(); iter.hasNext(); ) {
+      Screen refScreen = (Screen)iter.next();
+      if (refScreen.id == id)
+        iter.remove();
+    }
+  }
+
+  public static final boolean validate(int fb_width, int fb_height) {
+      List<Integer> seen_ids = new ArrayList<Integer>();
+      Rect fb_rect = new Rect();
+
+      if (screens.isEmpty())
+        return false;
+      if (num_screens() > 255)
+        return false;
+
+      fb_rect.setXYWH(0, 0, fb_width, fb_height);
+
+      for (Iterator iter = screens.iterator(); iter.hasNext(); ) {
+        Screen refScreen = (Screen)iter.next();
+        if (refScreen.dimensions.is_empty())
+          return false;
+        if (!refScreen.dimensions.enclosed_by(fb_rect))
+          return false;
+        //if (seen_ids.lastIndexOf(refScreen.id) != seen_ids.get(-1))
+        //  return false;
+        seen_ids.add(refScreen.id);
+      }
+
+      return true;
+  }
+
+  public final void debug_print() {
+    for (Iterator iter = screens.iterator(); iter.hasNext(); ) {
+      Screen refScreen = (Screen)iter.next();
+      vlog.error("    "+refScreen.id+" (0x"+refScreen.id+"): "+
+                refScreen.dimensions.width()+"x"+refScreen.dimensions.height()+
+                "+"+refScreen.dimensions.tl.x+"+"+refScreen.dimensions.tl.y+
+                " (flags 0x"+refScreen.flags+")");
+    }
+  }
+
+  // FIXME: List order shouldn't matter
+  //inline bool operator(const ScreenSet& r) const { return screens == r.screens; }
+  //inline bool operator(const ScreenSet& r) const { return screens != r.screens; }
+
+  public static List<Screen> screens;
+
+  static LogWriter vlog = new LogWriter("ScreenSet");
+
+}
+
diff --git a/java/src/com/tigervnc/rfb/Security.java b/java/src/com/tigervnc/rfb/Security.java
new file mode 100644
index 0000000..5e572e3
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/Security.java
@@ -0,0 +1,189 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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.
+ */
+
+//
+// SecTypes.java - constants for the various security types.
+//
+
+package com.tigervnc.rfb;
+import java.util.*;
+
+public class Security {
+
+  public static final int secTypeInvalid   = 0;
+  public static final int secTypeNone      = 1;
+  public static final int secTypeVncAuth   = 2;
+
+  public static final int secTypeRA2       = 5;
+  public static final int secTypeRA2ne     = 6;
+
+  public static final int secTypeSSPI      = 7;
+  public static final int secTypeSSPIne    = 8;
+
+  public static final int secTypeTight     = 16;
+  public static final int secTypeUltra     = 17;
+  public static final int secTypeTLS       = 18;
+  public static final int secTypeVeNCrypt  = 19;
+  public static final int secTypeManaged   = 20;
+
+  /* VeNCrypt subtypes */
+  public static final int secTypePlain     = 256;
+  public static final int secTypeTLSNone   = 257;
+  public static final int secTypeTLSVnc    = 258;
+  public static final int secTypeTLSPlain  = 259;
+  public static final int secTypeX509None  = 260;
+  public static final int secTypeX509Vnc   = 261;
+  public static final int secTypeX509Plain = 262;
+
+  // result types
+
+  public static final int secResultOK = 0;
+  public static final int secResultFailed = 1;
+  public static final int secResultTooMany = 2; // deprecated
+
+  public Security(StringParameter secTypes)
+  {
+    String secTypesStr;
+
+    secTypesStr = secTypes.getData();
+    enabledSecTypes = parseSecTypes(secTypesStr);
+
+    secTypesStr = null;
+  }
+
+  public static List<Integer> enabledSecTypes = new ArrayList<Integer>();
+
+  public static final List<Integer> GetEnabledSecTypes()
+  {
+    List<Integer> result = new ArrayList<Integer>();
+
+    result.add(secTypeVeNCrypt);
+    for (Iterator i = enabledSecTypes.iterator(); i.hasNext(); ) {
+      int refType = (Integer)i.next();
+      if (refType < 0x100)
+        result.add(refType);
+    }
+    
+    return (result);
+  }
+
+  public static final List<Integer> GetEnabledExtSecTypes()
+  {
+    List<Integer> result = new ArrayList<Integer>();
+
+    for (Iterator i = enabledSecTypes.iterator(); i.hasNext(); ) {
+      int refType = (Integer)i.next();
+      if (refType != secTypeVeNCrypt) /* Do not include VeNCrypt to avoid loops */
+        result.add(refType);
+    }
+
+    return (result);
+  }
+
+  public static final void EnableSecType(int secType)
+  {
+
+    for (Iterator i = enabledSecTypes.iterator(); i.hasNext(); )
+      if ((Integer)i.next() == secType)
+        return;
+
+    enabledSecTypes.add(secType);
+  }
+
+  public boolean IsSupported(int secType)
+  {
+    Iterator i;
+  
+    for (i = enabledSecTypes.iterator(); i.hasNext(); )
+     if ((Integer)i.next() == secType)
+       return true;
+    if (secType == secTypeVeNCrypt)
+     return true;
+  
+    return false;
+  }
+
+  public static void DisableSecType(int secType) { enabledSecTypes.remove(secType); }
+
+  public static int secTypeNum(String name) {
+    if (name.equalsIgnoreCase("None"))      return secTypeNone;
+    if (name.equalsIgnoreCase("VncAuth"))   return secTypeVncAuth;
+    if (name.equalsIgnoreCase("Tight"))	    return secTypeTight;
+    if (name.equalsIgnoreCase("RA2"))       return secTypeRA2;
+    if (name.equalsIgnoreCase("RA2ne"))	    return secTypeRA2ne;
+    if (name.equalsIgnoreCase("SSPI"))      return secTypeSSPI;
+    if (name.equalsIgnoreCase("SSPIne"))	  return secTypeSSPIne;
+    //if (name.equalsIgnoreCase("ultra"))	    return secTypeUltra;
+    //if (name.equalsIgnoreCase("TLS"))	      return secTypeTLS;
+    if (name.equalsIgnoreCase("VeNCrypt"))  return secTypeVeNCrypt;
+    if (name.equalsIgnoreCase("Managed"))	  return secTypeManaged;
+
+    /* VeNCrypt subtypes */
+    if (name.equalsIgnoreCase("Plain"))	    return secTypePlain;
+    if (name.equalsIgnoreCase("TLSNone"))	  return secTypeTLSNone;
+    if (name.equalsIgnoreCase("TLSVnc"))	  return secTypeTLSVnc;
+    if (name.equalsIgnoreCase("TLSPlain"))	return secTypeTLSPlain;
+    if (name.equalsIgnoreCase("X509None"))	return secTypeX509None;
+    if (name.equalsIgnoreCase("X509Vnc"))	  return secTypeX509Vnc;
+    if (name.equalsIgnoreCase("X509Plain")) return secTypeX509Plain;
+
+    return secTypeInvalid;
+  }
+
+  public static String secTypeName(int num) {
+    switch (num) {
+    case secTypeNone:       return "None";
+    case secTypeVncAuth:    return "VncAuth";
+    case secTypeTight:      return "Tight";
+    case secTypeRA2:        return "RA2";
+    case secTypeRA2ne:      return "RA2ne";
+    case secTypeSSPI:       return "SSPI";
+    case secTypeSSPIne:     return "SSPIne";
+    //case secTypeUltra:      return "Ultra";
+    //case secTypeTLS:        return "TLS";
+    case secTypeVeNCrypt:   return "VeNCrypt";
+    case secTypeManaged:    return "Managed";
+
+    /* VeNCrypt subtypes */
+    case secTypePlain:      return "Plain";
+    case secTypeTLSNone:    return "TLSNone";
+    case secTypeTLSVnc:     return "TLSVnc";
+    case secTypeTLSPlain:   return "TLSPlain";
+    case secTypeX509None:   return "X509None";
+    case secTypeX509Vnc:    return "X509Vnc";
+    case secTypeX509Plain:  return "X509Plain";
+    default:                return "[unknown secType]";
+    }
+  }
+
+  public final static List<Integer> parseSecTypes(String types_)
+  {
+    List<Integer> result = new ArrayList<Integer>();
+    String[] types = types_.split(",");
+    for (int i = 0; i < types.length; i++) {
+      int typeNum = secTypeNum(types[i]);
+      if (typeNum != secTypeInvalid)
+        result.add(typeNum);
+    }
+    return (result);
+  }
+
+  public final void SetSecTypes(List<Integer> secTypes) { enabledSecTypes = secTypes; }
+
+  static LogWriter vlog = new LogWriter("Security");
+}
diff --git a/java/src/com/tigervnc/rfb/SecurityClient.java b/java/src/com/tigervnc/rfb/SecurityClient.java
new file mode 100644
index 0000000..90c35d8
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/SecurityClient.java
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2010 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
+ * 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 SecurityClient extends Security {
+
+  public SecurityClient() { super(secTypes); }
+
+  public CSecurity GetCSecurity(int secType)
+  {
+    //assert (CSecurity::upg != NULL); /* (upg == NULL) means bug in the viewer */
+    //assert (CSecurityTLS::msg != NULL);
+  
+    if (!IsSupported(secType))
+      throw new Exception("Security type not supported");
+  
+    switch (secType) {
+    case Security.secTypeManaged: return (new CSecurityManaged());
+    case Security.secTypeNone: return (new CSecurityNone());
+    case Security.secTypeVncAuth: return (new CSecurityVncAuth());
+    case Security.secTypeVeNCrypt: return (new CSecurityVeNCrypt(this));
+    case Security.secTypePlain: return (new CSecurityPlain());
+    case Security.secTypeTLSNone:
+      return (new CSecurityStack(secTypeTLSNone, "TLS with no password",
+  			      new CSecurityTLS(true), null));
+    case Security.secTypeTLSVnc:
+      return (new CSecurityStack(secTypeTLSVnc, "TLS with VNCAuth",
+  			      new CSecurityTLS(true), new CSecurityVncAuth()));
+    case Security.secTypeTLSPlain:
+      return (new CSecurityStack(secTypeTLSPlain, "TLS with Username/Password",
+  			      new CSecurityTLS(true), new CSecurityPlain()));
+    case Security.secTypeX509None:
+      return (new CSecurityStack(secTypeX509None, "X509 with no password",
+  			      new CSecurityTLS(false), null));
+    case Security.secTypeX509Vnc:
+      return (new CSecurityStack(secTypeX509None, "X509 with VNCAuth",
+  			      new CSecurityTLS(false), new CSecurityVncAuth()));
+    case Security.secTypeX509Plain:
+      return (new CSecurityStack(secTypeX509Plain, "X509 with Username/Password",
+  			      new CSecurityTLS(false), new CSecurityPlain()));
+    default:
+      throw new Exception("Security type not supported");
+    }
+  
+  }
+
+  public static void setDefaults()
+  {
+      CSecurityTLS.setDefaults();
+  }
+
+  //UserPasswdGetter upg = null;
+  String msg = null;
+
+  static StringParameter secTypes 
+  = new StringParameter("SecurityTypes",
+                        "Specify which security scheme to use (None, VncAuth)",
+                        "Managed,X509Plain,TLSPlain,X509Vnc,TLSVnc,X509None,TLSNone,VncAuth,None");
+
+}
diff --git a/java/src/com/tigervnc/rfb/StringParameter.java b/java/src/com/tigervnc/rfb/StringParameter.java
new file mode 100644
index 0000000..4f704d9
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/StringParameter.java
@@ -0,0 +1,46 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 StringParameter extends VoidParameter {

+  public StringParameter(String name_, String desc_, String v) {

+    super(name_, desc_);

+    value = v;

+    defValue = v;

+  }

+

+  public boolean setParam(String v) {

+    value = v;

+    return value != null;

+  }

+

+  public boolean setDefaultStr(String v) {

+    value = defValue = v;

+    return defValue != null;

+  }

+

+  public String getDefaultStr() { return defValue; }

+  public String getValueStr() { return value; }

+

+  public String getValue() { return value; }

+  public String getData() { return value; }

+

+  protected String value;

+  protected String defValue;

+}

diff --git a/java/src/com/tigervnc/rfb/TightDecoder.java b/java/src/com/tigervnc/rfb/TightDecoder.java
new file mode 100644
index 0000000..1065feb
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/TightDecoder.java
@@ -0,0 +1,427 @@
+/* Copyright (C) 2000-2003 Constantin Kaplinsky.  All Rights Reserved.
+ * Copyright (C) 2004-2005 Cendio AB. 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;
+
+import com.tigervnc.rdr.InStream;
+import com.tigervnc.rdr.ZlibInStream;
+import java.awt.image.PixelGrabber;
+import java.awt.Image;
+import java.util.ArrayList;
+
+public class TightDecoder extends Decoder {
+
+  final static int TIGHT_MAX_WIDTH = 2048;
+
+  // Compression control
+  final static int rfbTightExplicitFilter = 0x04;
+  final static int rfbTightFill = 0x08;
+  final static int rfbTightJpeg = 0x09;
+  final static int rfbTightMaxSubencoding = 0x09;
+
+  // Filters to improve compression efficiency
+  final static int rfbTightFilterCopy = 0x00;
+  final static int rfbTightFilterPalette = 0x01;
+  final static int rfbTightFilterGradient = 0x02;
+  final static int rfbTightMinToCompress = 12;
+
+  public TightDecoder(CMsgReader reader_) { 
+    reader = reader_; 
+    zis = new ZlibInStream[4];
+    for (int i = 0; i < 4; i++)
+      zis[i] = new ZlibInStream();
+  }
+
+  public void readRect(Rect r, CMsgHandler handler) 
+  {
+    InStream is = reader.getInStream();
+    int[] buf = reader.getImageBuf(r.width() * r.height());
+    boolean cutZeros = false;
+    PixelFormat myFormat = handler.cp.pf();
+    int bpp = handler.cp.pf().bpp;
+    if (bpp == 32) {
+      if (myFormat.is888()) {
+        cutZeros = true;
+      }
+    }
+
+    int comp_ctl = is.readU8();
+
+    int bytesPerPixel = handler.cp.pf().bpp / 8;
+    boolean bigEndian = handler.cp.pf().bigEndian;
+
+    // Flush zlib streams if we are told by the server to do so.
+    for (int i = 0; i < 4; i++) {
+      if ((comp_ctl & 1) != 0) {
+        zis[i].reset();
+      }
+      comp_ctl >>= 1;
+    }
+
+    // "Fill" compression type.
+    if (comp_ctl == rfbTightFill) {
+      int pix;
+      if (cutZeros) {
+        byte[] elem = new byte[3];
+        is.readBytes(elem, 0, 3);
+        if (bigEndian) {
+          pix =
+            (elem[2] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[0] & 0xFF) | 0xFF << 24;
+        } else {
+          pix =
+            (elem[0] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[2] & 0xFF) | 0xFF << 24;
+        }
+      } else {
+        pix = (bpp == 8) ? is.readOpaque8() : is.readOpaque24B();
+      }
+      handler.fillRect(r, pix);
+      return;
+    }
+
+    // "JPEG" compression type.
+    if (comp_ctl == rfbTightJpeg) {
+      // Read length
+      int compressedLen = is.readCompactLength();
+      if (compressedLen <= 0)
+        vlog.info("Incorrect data received from the server.");
+
+      // Allocate netbuf and read in data
+      byte[] netbuf = new byte[compressedLen];
+      is.readBytes(netbuf, 0, compressedLen);
+
+      // Create an Image object from the JPEG data.
+      Image jpeg = java.awt.Toolkit.getDefaultToolkit().createImage(netbuf);
+      PixelGrabber pg = new PixelGrabber(jpeg, 0, 0, r.width(), r.height(), true);
+      try {
+        boolean ret = pg.grabPixels();
+        if (!ret)
+          vlog.info("failed to grab pixels");
+      } catch (InterruptedException e) {
+        e.printStackTrace();
+      }
+      Object pixels = pg.getPixels();
+      buf = (pixels instanceof byte[]) ? 
+        convertByteArrayToIntArray((byte[])pixels) : (int[])pixels;
+      handler.imageRect(r, buf);
+      return;
+    }
+
+    // Quit on unsupported compression type.
+    if (comp_ctl > rfbTightMaxSubencoding) {
+      throw new Exception("TightDecoder: bad subencoding value received");
+    }
+
+    // "Basic" compression type.
+    int palSize = 0;
+    int[] palette = new int[256];
+    boolean useGradient = false;
+
+    if ((comp_ctl & rfbTightExplicitFilter) != 0) {
+      int filterId = is.readU8();
+
+      switch (filterId) {
+      case rfbTightFilterPalette:
+        palSize = is.readU8() + 1;
+        if (cutZeros) {
+          byte[] elem = new byte[3];
+          for (int i = 0; i < palSize; i++) {
+            is.readBytes(elem, 0, 3);
+            if (bigEndian) {
+              palette[i] =
+                (elem[2] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[0] & 0xFF) | 0xFF << 24;
+            } else {
+              palette[i] =
+                (elem[0] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[2] & 0xFF) | 0xFF << 24;
+            }
+          }
+        } else {
+          for (int i = 0; i < palSize; i++) {
+            palette[i] = (bpp == 8) ? is.readOpaque8() : is.readOpaque24B();
+          }
+        }
+        break;
+      case rfbTightFilterGradient:
+        useGradient = true;
+        break;
+      case rfbTightFilterCopy:
+        break;
+      default:
+        throw new Exception("TightDecoder: unknown filter code recieved");
+      }
+    }
+
+    int bppp = bpp;
+    if (palSize != 0) {
+      bppp = (palSize <= 2) ? 1 : 8;
+    } else if (cutZeros) {
+      bppp = 24;
+    }
+
+    // Determine if the data should be decompressed or just copied.
+    int rowSize = (r.width() * bppp + 7) / 8;
+    int dataSize = r.height() * rowSize;
+    int streamId = -1;
+    InStream input;
+    if (dataSize < rfbTightMinToCompress) {
+      input = is;
+    } else {
+      int length = is.readCompactLength();
+      streamId = comp_ctl & 0x03;
+      zis[streamId].setUnderlying(is, length);
+      input = (ZlibInStream)zis[streamId];
+    }
+
+    if (palSize == 0) {
+      // Truecolor data.
+      if (useGradient) {
+        vlog.info("useGradient");
+        if (bpp == 32 && cutZeros) {
+          vlog.info("FilterGradient24");
+          FilterGradient24(r, input, dataSize, buf, handler);
+        } else {
+          vlog.info("FilterGradient");
+          FilterGradient(r, input, dataSize, buf, handler);
+        }
+      } else {
+        if (cutZeros) {
+          byte[] elem = new byte[3];
+          for (int i = 0; i < r.width() * r.height(); i++) {
+            input.readBytes(elem, 0, 3);
+            if (bigEndian) {
+              buf[i] =
+                (elem[2] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[0] & 0xFF) | 0xFF << 24;
+            } else {
+              buf[i] =
+                (elem[0] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[2] & 0xFF) | 0xFF << 24;
+            }
+          }
+        } else {
+          for (int i = 0; i < r.width() * r.height(); i++) {
+            buf[i] = input.readU8();
+          }
+        }
+      }
+    } else {
+      int ptr = 0;
+      int bits;
+      if (palSize <= 2) {
+        // 2-color palette
+        int dw = (r.width() + 7) / 8;
+        for (int dy = 0; dy < r.height(); dy++) {
+          for (int dx = 0; dx < r.width() / 8; dx++) {
+            bits = input.readU8();
+            for(int b = 7; b >= 0; b--) {
+              buf[ptr++] = palette[bits >> b & 1];
+            }
+          }
+          if (r.width() % 8 != 0) {
+            bits = input.readU8();
+            for (int b = 7; b >= 8 - r.width() % 8; b--) {
+              buf[ptr++] = palette[bits >> b & 1];
+            }
+          }
+        }
+      } else {
+        // 256-color palette
+        for (int dy = 0; dy < r.height(); dy++) {
+          for (int dx = 0; dx < r.width(); dx++) {
+            buf[ptr++] = palette[input.readU8()];
+          }
+        }
+      }
+    } 
+
+    handler.imageRect(r, buf);
+
+    if (streamId != -1) {
+      zis[streamId].reset();
+    }
+  }
+
+  private CMsgReader reader;
+  private ZlibInStream[] zis;
+  static LogWriter vlog = new LogWriter("TightDecoder");
+
+  private static int convertByteArrayToInt(byte[] bytes) {
+    return (bytes[0] << 32) | (bytes[1] << 24) | (bytes[2] << 16) | (bytes[3] << 8) | bytes[4];
+  }
+
+  private static byte[] convertIntToByteArray(int integer) {
+    byte[] bytes = new byte[4];
+    bytes[0] =(byte)( integer >> 24 );
+    bytes[1] =(byte)( (integer << 8) >> 24 );
+    bytes[2] =(byte)( (integer << 16) >> 24 );
+    bytes[3] =(byte)( (integer << 24) >> 24 );
+    return bytes;
+  }
+  private static int[] convertByteArrayToIntArray(byte[] bytes) {
+    vlog.info("convertByteArrayToIntArray");
+    ArrayList integers = new ArrayList();
+    for (int index = 0; index < bytes.length; index += 4) {
+      byte[] fourBytes = new byte[4];
+      fourBytes[0] = bytes[index];
+      fourBytes[1] = bytes[index+1];
+      fourBytes[2] = bytes[index+2];
+      fourBytes[3] = bytes[index+3];
+      int integer = convertByteArrayToInt(fourBytes);
+      integers.add(new Integer(integer));
+    }
+    int[] ints = new int[bytes.length/4];
+    for (int index = 0; index < integers.size() ; index++) {
+      ints[index] = ((Integer)integers.get(index)).intValue();
+    }
+    return ints;
+  }
+
+  private static byte[] convertIntArrayToByteArray(int[] integers) {
+    byte[] bytes = new byte[integers.length*4];
+    for (int index = 0; index < integers.length; index++) {
+      byte[] integerBytes = convertIntToByteArray(integers[index]);
+      bytes[index*4] = integerBytes[0];
+      bytes[1 + (index*4)] = integerBytes[1];
+      bytes[2 + (index*4)] = integerBytes[2];
+      bytes[3 + (index*4)] = integerBytes[3];
+    }
+    return bytes;
+  }
+ 
+  //
+  // Decode data processed with the "Gradient" filter.
+  //
+
+  final private void FilterGradient24(Rect r, InStream is, int dataSize, int[] buf, CMsgHandler handler) {
+
+    int x, y, c;
+    int[] prevRow = new int[TIGHT_MAX_WIDTH * 3];
+    int[] thisRow = new int[TIGHT_MAX_WIDTH * 3];
+    int[] pix = new int[3];
+    int[] est = new int[3];
+
+    // Allocate netbuf and read in data
+    int[] netbuf = new int[dataSize];
+    for (int i = 0; i < dataSize; i++)
+      netbuf[i] = is.readU8();
+    //is.readBytes(netbuf, 0, dataSize);
+
+    PixelFormat myFormat = handler.cp.pf();
+    int rectHeight = r.height();
+    int rectWidth = r.width();
+
+    for (y = 0; y < rectHeight; y++) {
+      /* First pixel in a row */
+      for (c = 0; c < 3; c++) {
+        pix[c] = netbuf[y*rectWidth*3+c] + prevRow[c];
+        thisRow[c] = pix[c];
+      }
+      if (myFormat.bigEndian) {
+        buf[y*rectWidth] =
+          (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF) | 0xFF << 24;
+      } else {
+        buf[y*rectWidth] =
+          (pix[2] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[0] & 0xFF) | 0xFF << 24;
+      }
+
+      /* Remaining pixels of a row */
+      for (x = 1; x < rectWidth; x++) {
+        for (c = 0; c < 3; c++) {
+          est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
+          if (est[c] > 0xFF) {
+            est[c] = 0xFF;
+          } else if (est[c] < 0) {
+            est[c] = 0;
+          }
+          pix[c] = netbuf[(y*rectWidth+x)*3+c] + est[c];
+          thisRow[x*3+c] = pix[c];
+        }
+        if (myFormat.bigEndian) {
+          buf[y*rectWidth] =
+            (pix[2] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[0] & 0xFF) | 0xFF << 24;
+        } else {
+          buf[y*rectWidth] =
+            (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF) | 0xFF << 24;
+        }
+      }
+
+      System.arraycopy(thisRow, 0, prevRow, 0, prevRow.length);
+    }
+  }
+
+  final private void FilterGradient(Rect r, InStream is, int dataSize, int[] buf, CMsgHandler handler) {
+
+    int x, y, c;
+    int[] prevRow = new int[TIGHT_MAX_WIDTH];
+    int[] thisRow = new int[TIGHT_MAX_WIDTH];
+    int[] pix = new int[3];
+    int[] est = new int[3];
+
+    // Allocate netbuf and read in data
+    int[] netbuf = new int[dataSize];
+    for (int i = 0; i < dataSize; i++)
+      netbuf[i] = is.readU8();
+    //is.readBytes(netbuf, 0, dataSize);
+
+    PixelFormat myFormat = handler.cp.pf();
+    int rectHeight = r.height();
+    int rectWidth = r.width();
+
+    for (y = 0; y < rectHeight; y++) {
+      /* First pixel in a row */
+      if (myFormat.bigEndian) {
+        buf[y*rectWidth] =
+          (pix[2] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[0] & 0xFF) | 0xFF << 24;
+      } else {
+        buf[y*rectWidth] =
+          (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF) | 0xFF << 24;
+      }
+      for (c = 0; c < 3; c++)
+        pix[c] += prevRow[c];
+
+      /* Remaining pixels of a row */
+      for (x = 1; x < rectWidth; x++) {
+        for (c = 0; c < 3; c++) {
+          est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
+          if (est[c] > 255) {
+            est[c] = 255;
+          } else if (est[c] < 0) {
+            est[c] = 0;
+          }
+        }
+
+        // FIXME?
+        System.arraycopy(pix, 0, netbuf, 0, netbuf.length);
+        for (c = 0; c < 3; c++)
+          pix[c] += est[c];
+
+        System.arraycopy(thisRow, x*3, pix, 0, pix.length);
+
+        if (myFormat.bigEndian) {
+          buf[y*rectWidth+x] =
+            (pix[2] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[0] & 0xFF) | 0xFF << 24;
+        } else {
+          buf[y*rectWidth+x] =
+            (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF) | 0xFF << 24;
+        }
+
+      }
+
+      System.arraycopy(thisRow, 0, prevRow, 0, prevRow.length);
+    }
+  }
+
+}
diff --git a/java/src/com/tigervnc/rfb/UnicodeToKeysym.java b/java/src/com/tigervnc/rfb/UnicodeToKeysym.java
new file mode 100644
index 0000000..44f61ba
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/UnicodeToKeysym.java
@@ -0,0 +1,795 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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.
+ */
+//
+// UnicodeToKeysym - provides a translate() method to convert from unicode
+// characters to the equivalent X keysym.
+//
+
+package com.tigervnc.rfb;
+
+public class UnicodeToKeysym {
+
+  public static short[][] table = {
+    { 0x03c0, 0x0100 },
+    { 0x03e0, 0x0101 },
+    { 0x01c3, 0x0102 },
+    { 0x01e3, 0x0103 },
+    { 0x01a1, 0x0104 },
+    { 0x01b1, 0x0105 },
+    { 0x01c6, 0x0106 },
+    { 0x01e6, 0x0107 },
+    { 0x02c6, 0x0108 },
+    { 0x02e6, 0x0109 },
+    { 0x02c5, 0x010a },
+    { 0x02e5, 0x010b },
+    { 0x01c8, 0x010c },
+    { 0x01e8, 0x010d },
+    { 0x01cf, 0x010e },
+    { 0x01ef, 0x010f },
+    { 0x01d0, 0x0110 },
+    { 0x01f0, 0x0111 },
+    { 0x03aa, 0x0112 },
+    { 0x03ba, 0x0113 },
+    { 0x03cc, 0x0116 },
+    { 0x03ec, 0x0117 },
+    { 0x01ca, 0x0118 },
+    { 0x01ea, 0x0119 },
+    { 0x01cc, 0x011a },
+    { 0x01ec, 0x011b },
+    { 0x02d8, 0x011c },
+    { 0x02f8, 0x011d },
+    { 0x02ab, 0x011e },
+    { 0x02bb, 0x011f },
+    { 0x02d5, 0x0120 },
+    { 0x02f5, 0x0121 },
+    { 0x03ab, 0x0122 },
+    { 0x03bb, 0x0123 },
+    { 0x02a6, 0x0124 },
+    { 0x02b6, 0x0125 },
+    { 0x02a1, 0x0126 },
+    { 0x02b1, 0x0127 },
+    { 0x03a5, 0x0128 },
+    { 0x03b5, 0x0129 },
+    { 0x03cf, 0x012a },
+    { 0x03ef, 0x012b },
+    { 0x03c7, 0x012e },
+    { 0x03e7, 0x012f },
+    { 0x02a9, 0x0130 },
+    { 0x02b9, 0x0131 },
+    { 0x02ac, 0x0134 },
+    { 0x02bc, 0x0135 },
+    { 0x03d3, 0x0136 },
+    { 0x03f3, 0x0137 },
+    { 0x03a2, 0x0138 },
+    { 0x01c5, 0x0139 },
+    { 0x01e5, 0x013a },
+    { 0x03a6, 0x013b },
+    { 0x03b6, 0x013c },
+    { 0x01a5, 0x013d },
+    { 0x01b5, 0x013e },
+    { 0x01a3, 0x0141 },
+    { 0x01b3, 0x0142 },
+    { 0x01d1, 0x0143 },
+    { 0x01f1, 0x0144 },
+    { 0x03d1, 0x0145 },
+    { 0x03f1, 0x0146 },
+    { 0x01d2, 0x0147 },
+    { 0x01f2, 0x0148 },
+    { 0x03bd, 0x014a },
+    { 0x03bf, 0x014b },
+    { 0x03d2, 0x014c },
+    { 0x03f2, 0x014d },
+    { 0x01d5, 0x0150 },
+    { 0x01f5, 0x0151 },
+    { 0x13bc, 0x0152 },
+    { 0x13bd, 0x0153 },
+    { 0x01c0, 0x0154 },
+    { 0x01e0, 0x0155 },
+    { 0x03a3, 0x0156 },
+    { 0x03b3, 0x0157 },
+    { 0x01d8, 0x0158 },
+    { 0x01f8, 0x0159 },
+    { 0x01a6, 0x015a },
+    { 0x01b6, 0x015b },
+    { 0x02de, 0x015c },
+    { 0x02fe, 0x015d },
+    { 0x01aa, 0x015e },
+    { 0x01ba, 0x015f },
+    { 0x01a9, 0x0160 },
+    { 0x01b9, 0x0161 },
+    { 0x01de, 0x0162 },
+    { 0x01fe, 0x0163 },
+    { 0x01ab, 0x0164 },
+    { 0x01bb, 0x0165 },
+    { 0x03ac, 0x0166 },
+    { 0x03bc, 0x0167 },
+    { 0x03dd, 0x0168 },
+    { 0x03fd, 0x0169 },
+    { 0x03de, 0x016a },
+    { 0x03fe, 0x016b },
+    { 0x02dd, 0x016c },
+    { 0x02fd, 0x016d },
+    { 0x01d9, 0x016e },
+    { 0x01f9, 0x016f },
+    { 0x01db, 0x0170 },
+    { 0x01fb, 0x0171 },
+    { 0x03d9, 0x0172 },
+    { 0x03f9, 0x0173 },
+    { 0x13be, 0x0178 },
+    { 0x01ac, 0x0179 },
+    { 0x01bc, 0x017a },
+    { 0x01af, 0x017b },
+    { 0x01bf, 0x017c },
+    { 0x01ae, 0x017d },
+    { 0x01be, 0x017e },
+    { 0x08f6, 0x0192 },
+    { 0x01b7, 0x02c7 },
+    { 0x01a2, 0x02d8 },
+    { 0x01ff, 0x02d9 },
+    { 0x01b2, 0x02db },
+    { 0x01bd, 0x02dd },
+    { 0x07ae, 0x0385 },
+    { 0x07a1, 0x0386 },
+    { 0x07a2, 0x0388 },
+    { 0x07a3, 0x0389 },
+    { 0x07a4, 0x038a },
+    { 0x07a7, 0x038c },
+    { 0x07a8, 0x038e },
+    { 0x07ab, 0x038f },
+    { 0x07b6, 0x0390 },
+    { 0x07c1, 0x0391 },
+    { 0x07c2, 0x0392 },
+    { 0x07c3, 0x0393 },
+    { 0x07c4, 0x0394 },
+    { 0x07c5, 0x0395 },
+    { 0x07c6, 0x0396 },
+    { 0x07c7, 0x0397 },
+    { 0x07c8, 0x0398 },
+    { 0x07c9, 0x0399 },
+    { 0x07ca, 0x039a },
+    { 0x07cb, 0x039b },
+    { 0x07cc, 0x039c },
+    { 0x07cd, 0x039d },
+    { 0x07ce, 0x039e },
+    { 0x07cf, 0x039f },
+    { 0x07d0, 0x03a0 },
+    { 0x07d1, 0x03a1 },
+    { 0x07d2, 0x03a3 },
+    { 0x07d4, 0x03a4 },
+    { 0x07d5, 0x03a5 },
+    { 0x07d6, 0x03a6 },
+    { 0x07d7, 0x03a7 },
+    { 0x07d8, 0x03a8 },
+    { 0x07d9, 0x03a9 },
+    { 0x07a5, 0x03aa },
+    { 0x07a9, 0x03ab },
+    { 0x07b1, 0x03ac },
+    { 0x07b2, 0x03ad },
+    { 0x07b3, 0x03ae },
+    { 0x07b4, 0x03af },
+    { 0x07ba, 0x03b0 },
+    { 0x07e1, 0x03b1 },
+    { 0x07e2, 0x03b2 },
+    { 0x07e3, 0x03b3 },
+    { 0x07e4, 0x03b4 },
+    { 0x07e5, 0x03b5 },
+    { 0x07e6, 0x03b6 },
+    { 0x07e7, 0x03b7 },
+    { 0x07e8, 0x03b8 },
+    { 0x07e9, 0x03b9 },
+    { 0x07ea, 0x03ba },
+    { 0x07eb, 0x03bb },
+    { 0x07ec, 0x03bc },
+    { 0x07ed, 0x03bd },
+    { 0x07ee, 0x03be },
+    { 0x07ef, 0x03bf },
+    { 0x07f0, 0x03c0 },
+    { 0x07f1, 0x03c1 },
+    { 0x07f3, 0x03c2 },
+    { 0x07f2, 0x03c3 },
+    { 0x07f4, 0x03c4 },
+    { 0x07f5, 0x03c5 },
+    { 0x07f6, 0x03c6 },
+    { 0x07f7, 0x03c7 },
+    { 0x07f8, 0x03c8 },
+    { 0x07f9, 0x03c9 },
+    { 0x07b5, 0x03ca },
+    { 0x07b9, 0x03cb },
+    { 0x07b7, 0x03cc },
+    { 0x07b8, 0x03cd },
+    { 0x07bb, 0x03ce },
+    { 0x06b3, 0x0401 },
+    { 0x06b1, 0x0402 },
+    { 0x06b2, 0x0403 },
+    { 0x06b4, 0x0404 },
+    { 0x06b5, 0x0405 },
+    { 0x06b6, 0x0406 },
+    { 0x06b7, 0x0407 },
+    { 0x06b8, 0x0408 },
+    { 0x06b9, 0x0409 },
+    { 0x06ba, 0x040a },
+    { 0x06bb, 0x040b },
+    { 0x06bc, 0x040c },
+    { 0x06be, 0x040e },
+    { 0x06bf, 0x040f },
+    { 0x06e1, 0x0410 },
+    { 0x06e2, 0x0411 },
+    { 0x06f7, 0x0412 },
+    { 0x06e7, 0x0413 },
+    { 0x06e4, 0x0414 },
+    { 0x06e5, 0x0415 },
+    { 0x06f6, 0x0416 },
+    { 0x06fa, 0x0417 },
+    { 0x06e9, 0x0418 },
+    { 0x06ea, 0x0419 },
+    { 0x06eb, 0x041a },
+    { 0x06ec, 0x041b },
+    { 0x06ed, 0x041c },
+    { 0x06ee, 0x041d },
+    { 0x06ef, 0x041e },
+    { 0x06f0, 0x041f },
+    { 0x06f2, 0x0420 },
+    { 0x06f3, 0x0421 },
+    { 0x06f4, 0x0422 },
+    { 0x06f5, 0x0423 },
+    { 0x06e6, 0x0424 },
+    { 0x06e8, 0x0425 },
+    { 0x06e3, 0x0426 },
+    { 0x06fe, 0x0427 },
+    { 0x06fb, 0x0428 },
+    { 0x06fd, 0x0429 },
+    { 0x06ff, 0x042a },
+    { 0x06f9, 0x042b },
+    { 0x06f8, 0x042c },
+    { 0x06fc, 0x042d },
+    { 0x06e0, 0x042e },
+    { 0x06f1, 0x042f },
+    { 0x06c1, 0x0430 },
+    { 0x06c2, 0x0431 },
+    { 0x06d7, 0x0432 },
+    { 0x06c7, 0x0433 },
+    { 0x06c4, 0x0434 },
+    { 0x06c5, 0x0435 },
+    { 0x06d6, 0x0436 },
+    { 0x06da, 0x0437 },
+    { 0x06c9, 0x0438 },
+    { 0x06ca, 0x0439 },
+    { 0x06cb, 0x043a },
+    { 0x06cc, 0x043b },
+    { 0x06cd, 0x043c },
+    { 0x06ce, 0x043d },
+    { 0x06cf, 0x043e },
+    { 0x06d0, 0x043f },
+    { 0x06d2, 0x0440 },
+    { 0x06d3, 0x0441 },
+    { 0x06d4, 0x0442 },
+    { 0x06d5, 0x0443 },
+    { 0x06c6, 0x0444 },
+    { 0x06c8, 0x0445 },
+    { 0x06c3, 0x0446 },
+    { 0x06de, 0x0447 },
+    { 0x06db, 0x0448 },
+    { 0x06dd, 0x0449 },
+    { 0x06df, 0x044a },
+    { 0x06d9, 0x044b },
+    { 0x06d8, 0x044c },
+    { 0x06dc, 0x044d },
+    { 0x06c0, 0x044e },
+    { 0x06d1, 0x044f },
+    { 0x06a3, 0x0451 },
+    { 0x06a1, 0x0452 },
+    { 0x06a2, 0x0453 },
+    { 0x06a4, 0x0454 },
+    { 0x06a5, 0x0455 },
+    { 0x06a6, 0x0456 },
+    { 0x06a7, 0x0457 },
+    { 0x06a8, 0x0458 },
+    { 0x06a9, 0x0459 },
+    { 0x06aa, 0x045a },
+    { 0x06ab, 0x045b },
+    { 0x06ac, 0x045c },
+    { 0x06ae, 0x045e },
+    { 0x06af, 0x045f },
+    { 0x0ce0, 0x05d0 },
+    { 0x0ce1, 0x05d1 },
+    { 0x0ce2, 0x05d2 },
+    { 0x0ce3, 0x05d3 },
+    { 0x0ce4, 0x05d4 },
+    { 0x0ce5, 0x05d5 },
+    { 0x0ce6, 0x05d6 },
+    { 0x0ce7, 0x05d7 },
+    { 0x0ce8, 0x05d8 },
+    { 0x0ce9, 0x05d9 },
+    { 0x0cea, 0x05da },
+    { 0x0ceb, 0x05db },
+    { 0x0cec, 0x05dc },
+    { 0x0ced, 0x05dd },
+    { 0x0cee, 0x05de },
+    { 0x0cef, 0x05df },
+    { 0x0cf0, 0x05e0 },
+    { 0x0cf1, 0x05e1 },
+    { 0x0cf2, 0x05e2 },
+    { 0x0cf3, 0x05e3 },
+    { 0x0cf4, 0x05e4 },
+    { 0x0cf5, 0x05e5 },
+    { 0x0cf6, 0x05e6 },
+    { 0x0cf7, 0x05e7 },
+    { 0x0cf8, 0x05e8 },
+    { 0x0cf9, 0x05e9 },
+    { 0x0cfa, 0x05ea },
+    { 0x05ac, 0x060c },
+    { 0x05bb, 0x061b },
+    { 0x05bf, 0x061f },
+    { 0x05c1, 0x0621 },
+    { 0x05c2, 0x0622 },
+    { 0x05c3, 0x0623 },
+    { 0x05c4, 0x0624 },
+    { 0x05c5, 0x0625 },
+    { 0x05c6, 0x0626 },
+    { 0x05c7, 0x0627 },
+    { 0x05c8, 0x0628 },
+    { 0x05c9, 0x0629 },
+    { 0x05ca, 0x062a },
+    { 0x05cb, 0x062b },
+    { 0x05cc, 0x062c },
+    { 0x05cd, 0x062d },
+    { 0x05ce, 0x062e },
+    { 0x05cf, 0x062f },
+    { 0x05d0, 0x0630 },
+    { 0x05d1, 0x0631 },
+    { 0x05d2, 0x0632 },
+    { 0x05d3, 0x0633 },
+    { 0x05d4, 0x0634 },
+    { 0x05d5, 0x0635 },
+    { 0x05d6, 0x0636 },
+    { 0x05d7, 0x0637 },
+    { 0x05d8, 0x0638 },
+    { 0x05d9, 0x0639 },
+    { 0x05da, 0x063a },
+    { 0x05e0, 0x0640 },
+    { 0x05e1, 0x0641 },
+    { 0x05e2, 0x0642 },
+    { 0x05e3, 0x0643 },
+    { 0x05e4, 0x0644 },
+    { 0x05e5, 0x0645 },
+    { 0x05e6, 0x0646 },
+    { 0x05e7, 0x0647 },
+    { 0x05e8, 0x0648 },
+    { 0x05e9, 0x0649 },
+    { 0x05ea, 0x064a },
+    { 0x05eb, 0x064b },
+    { 0x05ec, 0x064c },
+    { 0x05ed, 0x064d },
+    { 0x05ee, 0x064e },
+    { 0x05ef, 0x064f },
+    { 0x05f0, 0x0650 },
+    { 0x05f1, 0x0651 },
+    { 0x05f2, 0x0652 },
+    { 0x0da1, 0x0e01 },
+    { 0x0da2, 0x0e02 },
+    { 0x0da3, 0x0e03 },
+    { 0x0da4, 0x0e04 },
+    { 0x0da5, 0x0e05 },
+    { 0x0da6, 0x0e06 },
+    { 0x0da7, 0x0e07 },
+    { 0x0da8, 0x0e08 },
+    { 0x0da9, 0x0e09 },
+    { 0x0daa, 0x0e0a },
+    { 0x0dab, 0x0e0b },
+    { 0x0dac, 0x0e0c },
+    { 0x0dad, 0x0e0d },
+    { 0x0dae, 0x0e0e },
+    { 0x0daf, 0x0e0f },
+    { 0x0db0, 0x0e10 },
+    { 0x0db1, 0x0e11 },
+    { 0x0db2, 0x0e12 },
+    { 0x0db3, 0x0e13 },
+    { 0x0db4, 0x0e14 },
+    { 0x0db5, 0x0e15 },
+    { 0x0db6, 0x0e16 },
+    { 0x0db7, 0x0e17 },
+    { 0x0db8, 0x0e18 },
+    { 0x0db9, 0x0e19 },
+    { 0x0dba, 0x0e1a },
+    { 0x0dbb, 0x0e1b },
+    { 0x0dbc, 0x0e1c },
+    { 0x0dbd, 0x0e1d },
+    { 0x0dbe, 0x0e1e },
+    { 0x0dbf, 0x0e1f },
+    { 0x0dc0, 0x0e20 },
+    { 0x0dc1, 0x0e21 },
+    { 0x0dc2, 0x0e22 },
+    { 0x0dc3, 0x0e23 },
+    { 0x0dc4, 0x0e24 },
+    { 0x0dc5, 0x0e25 },
+    { 0x0dc6, 0x0e26 },
+    { 0x0dc7, 0x0e27 },
+    { 0x0dc8, 0x0e28 },
+    { 0x0dc9, 0x0e29 },
+    { 0x0dca, 0x0e2a },
+    { 0x0dcb, 0x0e2b },
+    { 0x0dcc, 0x0e2c },
+    { 0x0dcd, 0x0e2d },
+    { 0x0dce, 0x0e2e },
+    { 0x0dcf, 0x0e2f },
+    { 0x0dd0, 0x0e30 },
+    { 0x0dd1, 0x0e31 },
+    { 0x0dd2, 0x0e32 },
+    { 0x0dd3, 0x0e33 },
+    { 0x0dd4, 0x0e34 },
+    { 0x0dd5, 0x0e35 },
+    { 0x0dd6, 0x0e36 },
+    { 0x0dd7, 0x0e37 },
+    { 0x0dd8, 0x0e38 },
+    { 0x0dd9, 0x0e39 },
+    { 0x0dda, 0x0e3a },
+    { 0x0ddf, 0x0e3f },
+    { 0x0de0, 0x0e40 },
+    { 0x0de1, 0x0e41 },
+    { 0x0de2, 0x0e42 },
+    { 0x0de3, 0x0e43 },
+    { 0x0de4, 0x0e44 },
+    { 0x0de5, 0x0e45 },
+    { 0x0de6, 0x0e46 },
+    { 0x0de7, 0x0e47 },
+    { 0x0de8, 0x0e48 },
+    { 0x0de9, 0x0e49 },
+    { 0x0dea, 0x0e4a },
+    { 0x0deb, 0x0e4b },
+    { 0x0dec, 0x0e4c },
+    { 0x0ded, 0x0e4d },
+    { 0x0df0, 0x0e50 },
+    { 0x0df1, 0x0e51 },
+    { 0x0df2, 0x0e52 },
+    { 0x0df3, 0x0e53 },
+    { 0x0df4, 0x0e54 },
+    { 0x0df5, 0x0e55 },
+    { 0x0df6, 0x0e56 },
+    { 0x0df7, 0x0e57 },
+    { 0x0df8, 0x0e58 },
+    { 0x0df9, 0x0e59 },
+    { 0x0ed4, 0x11a8 },
+    { 0x0ed5, 0x11a9 },
+    { 0x0ed6, 0x11aa },
+    { 0x0ed7, 0x11ab },
+    { 0x0ed8, 0x11ac },
+    { 0x0ed9, 0x11ad },
+    { 0x0eda, 0x11ae },
+    { 0x0edb, 0x11af },
+    { 0x0edc, 0x11b0 },
+    { 0x0edd, 0x11b1 },
+    { 0x0ede, 0x11b2 },
+    { 0x0edf, 0x11b3 },
+    { 0x0ee0, 0x11b4 },
+    { 0x0ee1, 0x11b5 },
+    { 0x0ee2, 0x11b6 },
+    { 0x0ee3, 0x11b7 },
+    { 0x0ee4, 0x11b8 },
+    { 0x0ee5, 0x11b9 },
+    { 0x0ee6, 0x11ba },
+    { 0x0ee7, 0x11bb },
+    { 0x0ee8, 0x11bc },
+    { 0x0ee9, 0x11bd },
+    { 0x0eea, 0x11be },
+    { 0x0eeb, 0x11bf },
+    { 0x0eec, 0x11c0 },
+    { 0x0eed, 0x11c1 },
+    { 0x0eee, 0x11c2 },
+    { 0x0ef8, 0x11eb },
+    { 0x0ef9, 0x11f0 },
+    { 0x0efa, 0x11f9 },
+    { 0x0aa2, 0x2002 },
+    { 0x0aa1, 0x2003 },
+    { 0x0aa3, 0x2004 },
+    { 0x0aa4, 0x2005 },
+    { 0x0aa5, 0x2007 },
+    { 0x0aa6, 0x2008 },
+    { 0x0aa7, 0x2009 },
+    { 0x0aa8, 0x200a },
+    { 0x0abb, 0x2012 },
+    { 0x0aaa, 0x2013 },
+    { 0x0aa9, 0x2014 },
+    { 0x07af, 0x2015 },
+    { 0x0cdf, 0x2017 },
+    { 0x0ad0, 0x2018 },
+    { 0x0ad1, 0x2019 },
+    { 0x0afd, 0x201a },
+    { 0x0ad2, 0x201c },
+    { 0x0ad3, 0x201d },
+    { 0x0afe, 0x201e },
+    { 0x0af1, 0x2020 },
+    { 0x0af2, 0x2021 },
+    { 0x0ae6, 0x2022 },
+    { 0x0aaf, 0x2025 },
+    { 0x0aae, 0x2026 },
+    { 0x0ad6, 0x2032 },
+    { 0x0ad7, 0x2033 },
+    { 0x0afc, 0x2038 },
+    { 0x047e, 0x203e },
+    { 0x0eff, 0x20a9 },
+    { 0x20ac, 0x20ac },
+    { 0x0ab8, 0x2105 },
+    { 0x06b0, 0x2116 },
+    { 0x0afb, 0x2117 },
+    { 0x0ad4, 0x211e },
+    { 0x0ac9, 0x2122 },
+    { 0x0ab0, 0x2153 },
+    { 0x0ab1, 0x2154 },
+    { 0x0ab2, 0x2155 },
+    { 0x0ab3, 0x2156 },
+    { 0x0ab4, 0x2157 },
+    { 0x0ab5, 0x2158 },
+    { 0x0ab6, 0x2159 },
+    { 0x0ab7, 0x215a },
+    { 0x0ac3, 0x215b },
+    { 0x0ac4, 0x215c },
+    { 0x0ac5, 0x215d },
+    { 0x0ac6, 0x215e },
+    { 0x08fb, 0x2190 },
+    { 0x08fc, 0x2191 },
+    { 0x08fd, 0x2192 },
+    { 0x08fe, 0x2193 },
+    { 0x08ce, 0x21d2 },
+    { 0x08cd, 0x21d4 },
+    { 0x08ef, 0x2202 },
+    { 0x08c5, 0x2207 },
+    { 0x0bca, 0x2218 },
+    { 0x08d6, 0x221a },
+    { 0x08c1, 0x221d },
+    { 0x08c2, 0x221e },
+    { 0x08de, 0x2227 },
+    { 0x08df, 0x2228 },
+    { 0x08dc, 0x2229 },
+    { 0x08dd, 0x222a },
+    { 0x08bf, 0x222b },
+    { 0x08c0, 0x2234 },
+    { 0x08c8, 0x223c },
+    { 0x08c9, 0x2243 },
+    { 0x08bd, 0x2260 },
+    { 0x08cf, 0x2261 },
+    { 0x08bc, 0x2264 },
+    { 0x08be, 0x2265 },
+    { 0x08da, 0x2282 },
+    { 0x08db, 0x2283 },
+    { 0x0bfc, 0x22a2 },
+    { 0x0bdc, 0x22a3 },
+    { 0x0bce, 0x22a4 },
+    { 0x0bc2, 0x22a5 },
+    { 0x0bd3, 0x2308 },
+    { 0x0bc4, 0x230a },
+    { 0x0afa, 0x2315 },
+    { 0x08a4, 0x2320 },
+    { 0x08a5, 0x2321 },
+    { 0x0abc, 0x2329 },
+    { 0x0abe, 0x232a },
+    { 0x0bcc, 0x2395 },
+    { 0x08ab, 0x239b },
+    { 0x08ac, 0x239d },
+    { 0x08ad, 0x239e },
+    { 0x08ae, 0x23a0 },
+    { 0x08a7, 0x23a1 },
+    { 0x08a8, 0x23a3 },
+    { 0x08a9, 0x23a4 },
+    { 0x08aa, 0x23a6 },
+    { 0x08af, 0x23a8 },
+    { 0x08b0, 0x23ac },
+    { 0x08a1, 0x23b7 },
+    { 0x09ef, 0x23ba },
+    { 0x09f0, 0x23bb },
+    { 0x09f2, 0x23bc },
+    { 0x09f3, 0x23bd },
+    { 0x09e2, 0x2409 },
+    { 0x09e5, 0x240a },
+    { 0x09e9, 0x240b },
+    { 0x09e3, 0x240c },
+    { 0x09e4, 0x240d },
+    { 0x09e8, 0x2424 },
+    { 0x09f1, 0x2500 },
+    { 0x09f8, 0x2502 },
+    { 0x09ec, 0x250c },
+    { 0x09eb, 0x2510 },
+    { 0x09ed, 0x2514 },
+    { 0x09ea, 0x2518 },
+    { 0x09f4, 0x251c },
+    { 0x09f5, 0x2524 },
+    { 0x09f7, 0x252c },
+    { 0x09f6, 0x2534 },
+    { 0x09ee, 0x253c },
+    { 0x09e1, 0x2592 },
+    { 0x0ae7, 0x25aa },
+    { 0x0ae1, 0x25ab },
+    { 0x0adb, 0x25ac },
+    { 0x0ae2, 0x25ad },
+    { 0x0adf, 0x25ae },
+    { 0x0acf, 0x25af },
+    { 0x0ae8, 0x25b2 },
+    { 0x0ae3, 0x25b3 },
+    { 0x0add, 0x25b6 },
+    { 0x0acd, 0x25b7 },
+    { 0x0ae9, 0x25bc },
+    { 0x0ae4, 0x25bd },
+    { 0x0adc, 0x25c0 },
+    { 0x0acc, 0x25c1 },
+    { 0x09e0, 0x25c6 },
+    { 0x0ace, 0x25cb },
+    { 0x0ade, 0x25cf },
+    { 0x0ae0, 0x25e6 },
+    { 0x0ae5, 0x2606 },
+    { 0x0af9, 0x260e },
+    { 0x0aca, 0x2613 },
+    { 0x0aea, 0x261c },
+    { 0x0aeb, 0x261e },
+    { 0x0af8, 0x2640 },
+    { 0x0af7, 0x2642 },
+    { 0x0aec, 0x2663 },
+    { 0x0aee, 0x2665 },
+    { 0x0aed, 0x2666 },
+    { 0x0af6, 0x266d },
+    { 0x0af5, 0x266f },
+    { 0x0af3, 0x2713 },
+    { 0x0af4, 0x2717 },
+    { 0x0ad9, 0x271d },
+    { 0x0af0, 0x2720 },
+    { 0x04a4, 0x3001 },
+    { 0x04a1, 0x3002 },
+    { 0x04a2, 0x300c },
+    { 0x04a3, 0x300d },
+    { 0x04de, 0x309b },
+    { 0x04df, 0x309c },
+    { 0x04a7, 0x30a1 },
+    { 0x04b1, 0x30a2 },
+    { 0x04a8, 0x30a3 },
+    { 0x04b2, 0x30a4 },
+    { 0x04a9, 0x30a5 },
+    { 0x04b3, 0x30a6 },
+    { 0x04aa, 0x30a7 },
+    { 0x04b4, 0x30a8 },
+    { 0x04ab, 0x30a9 },
+    { 0x04b5, 0x30aa },
+    { 0x04b6, 0x30ab },
+    { 0x04b7, 0x30ad },
+    { 0x04b8, 0x30af },
+    { 0x04b9, 0x30b1 },
+    { 0x04ba, 0x30b3 },
+    { 0x04bb, 0x30b5 },
+    { 0x04bc, 0x30b7 },
+    { 0x04bd, 0x30b9 },
+    { 0x04be, 0x30bb },
+    { 0x04bf, 0x30bd },
+    { 0x04c0, 0x30bf },
+    { 0x04c1, 0x30c1 },
+    { 0x04af, 0x30c3 },
+    { 0x04c2, 0x30c4 },
+    { 0x04c3, 0x30c6 },
+    { 0x04c4, 0x30c8 },
+    { 0x04c5, 0x30ca },
+    { 0x04c6, 0x30cb },
+    { 0x04c7, 0x30cc },
+    { 0x04c8, 0x30cd },
+    { 0x04c9, 0x30ce },
+    { 0x04ca, 0x30cf },
+    { 0x04cb, 0x30d2 },
+    { 0x04cc, 0x30d5 },
+    { 0x04cd, 0x30d8 },
+    { 0x04ce, 0x30db },
+    { 0x04cf, 0x30de },
+    { 0x04d0, 0x30df },
+    { 0x04d1, 0x30e0 },
+    { 0x04d2, 0x30e1 },
+    { 0x04d3, 0x30e2 },
+    { 0x04ac, 0x30e3 },
+    { 0x04d4, 0x30e4 },
+    { 0x04ad, 0x30e5 },
+    { 0x04d5, 0x30e6 },
+    { 0x04ae, 0x30e7 },
+    { 0x04d6, 0x30e8 },
+    { 0x04d7, 0x30e9 },
+    { 0x04d8, 0x30ea },
+    { 0x04d9, 0x30eb },
+    { 0x04da, 0x30ec },
+    { 0x04db, 0x30ed },
+    { 0x04dc, 0x30ef },
+    { 0x04a6, 0x30f2 },
+    { 0x04dd, 0x30f3 },
+    { 0x04a5, 0x30fb },
+    { 0x04b0, 0x30fc },
+    { 0x0ea1, 0x3131 },
+    { 0x0ea2, 0x3132 },
+    { 0x0ea3, 0x3133 },
+    { 0x0ea4, 0x3134 },
+    { 0x0ea5, 0x3135 },
+    { 0x0ea6, 0x3136 },
+    { 0x0ea7, 0x3137 },
+    { 0x0ea8, 0x3138 },
+    { 0x0ea9, 0x3139 },
+    { 0x0eaa, 0x313a },
+    { 0x0eab, 0x313b },
+    { 0x0eac, 0x313c },
+    { 0x0ead, 0x313d },
+    { 0x0eae, 0x313e },
+    { 0x0eaf, 0x313f },
+    { 0x0eb0, 0x3140 },
+    { 0x0eb1, 0x3141 },
+    { 0x0eb2, 0x3142 },
+    { 0x0eb3, 0x3143 },
+    { 0x0eb4, 0x3144 },
+    { 0x0eb5, 0x3145 },
+    { 0x0eb6, 0x3146 },
+    { 0x0eb7, 0x3147 },
+    { 0x0eb8, 0x3148 },
+    { 0x0eb9, 0x3149 },
+    { 0x0eba, 0x314a },
+    { 0x0ebb, 0x314b },
+    { 0x0ebc, 0x314c },
+    { 0x0ebd, 0x314d },
+    { 0x0ebe, 0x314e },
+    { 0x0ebf, 0x314f },
+    { 0x0ec0, 0x3150 },
+    { 0x0ec1, 0x3151 },
+    { 0x0ec2, 0x3152 },
+    { 0x0ec3, 0x3153 },
+    { 0x0ec4, 0x3154 },
+    { 0x0ec5, 0x3155 },
+    { 0x0ec6, 0x3156 },
+    { 0x0ec7, 0x3157 },
+    { 0x0ec8, 0x3158 },
+    { 0x0ec9, 0x3159 },
+    { 0x0eca, 0x315a },
+    { 0x0ecb, 0x315b },
+    { 0x0ecc, 0x315c },
+    { 0x0ecd, 0x315d },
+    { 0x0ece, 0x315e },
+    { 0x0ecf, 0x315f },
+    { 0x0ed0, 0x3160 },
+    { 0x0ed1, 0x3161 },
+    { 0x0ed2, 0x3162 },
+    { 0x0ed3, 0x3163 },
+    { 0x0eef, 0x316d },
+    { 0x0ef0, 0x3171 },
+    { 0x0ef1, 0x3178 },
+    { 0x0ef2, 0x317f },
+    { 0x0ef3, 0x3181 },
+    { 0x0ef4, 0x3184 },
+    { 0x0ef5, 0x3186 },
+    { 0x0ef6, 0x318d },
+    { 0x0ef7, 0x318e }
+  };
+
+  public static int translate(int unicode) {
+    if ((unicode >= 0x20 && unicode <= 0x7e) ||
+        (unicode >= 0xa0 && unicode <= 0xff))
+      return unicode;
+
+    int min = 0;
+    int max = table.length - 1;
+    int mid;
+
+    while (max >= min) {
+      mid = (min + max) / 2;
+      if (table[mid][1] < unicode)
+        min = mid + 1;
+      else if (table[mid][1] > unicode)
+        max = mid - 1;
+      else
+        return table[mid][0];
+    }
+
+    /* no matching Unicode value found */
+    return -1;
+  }
+}
diff --git a/java/src/com/tigervnc/rfb/UserMsgBox.java b/java/src/com/tigervnc/rfb/UserMsgBox.java
new file mode 100644
index 0000000..e83d995
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/UserMsgBox.java
@@ -0,0 +1,27 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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.
+ */
+
+// getUserPasswd() gets the username and password.  This might involve a
+// dialog, getpass(), etc.  The user buffer pointer can be null, in which case
+// no user name will be retrieved.
+
+package com.tigervnc.rfb;
+
+public interface UserMsgBox {
+  public boolean showMsgBox(int flags,String title, String text);
+}
diff --git a/java/src/com/tigervnc/rfb/UserPasswdGetter.java b/java/src/com/tigervnc/rfb/UserPasswdGetter.java
new file mode 100644
index 0000000..9796b66
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/UserPasswdGetter.java
@@ -0,0 +1,27 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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.
+ */
+
+// getUserPasswd() gets the username and password.  This might involve a
+// dialog, getpass(), etc.  The user buffer pointer can be null, in which case
+// no user name will be retrieved.
+
+package com.tigervnc.rfb;
+
+public interface UserPasswdGetter {
+  public boolean getUserPasswd(StringBuffer user, StringBuffer password);
+}
diff --git a/java/src/com/tigervnc/rfb/VncAuth.java b/java/src/com/tigervnc/rfb/VncAuth.java
new file mode 100644
index 0000000..cce8d81
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/VncAuth.java
@@ -0,0 +1,67 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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 VncAuth {
+
+  public static final int ok = 0;
+  public static final int failed = 1;
+  public static final int tooMany = 2; // deprecated
+
+  public static final int challengeSize = 16;
+
+  public static void encryptChallenge(byte[] challenge, String passwd) {
+    byte[] key = new byte[8];
+    for (int i = 0; i < 8 && i < passwd.length(); i++) {
+      key[i] = (byte)passwd.charAt(i);
+    }
+
+    DesCipher des = new DesCipher(key);
+
+    for (int j = 0; j < challengeSize; j += 8)
+      des.encrypt(challenge,j,challenge,j);
+  }
+
+  void obfuscatePasswd(String passwd, byte[] obfuscated) {
+    for (int i = 0; i < 8; i++) {
+      if (i < passwd.length())
+        obfuscated[i] = (byte)passwd.charAt(i);
+      else
+        obfuscated[i] = 0;
+    }
+    DesCipher des = new DesCipher(obfuscationKey);
+    des.encrypt(obfuscated,0,obfuscated,0);
+  }
+
+  String unobfuscatePasswd(byte[] obfuscated) {
+    DesCipher des = new DesCipher(obfuscationKey);
+    des.decrypt(obfuscated,0,obfuscated,0);
+    int len;
+    for (len = 0; len < 8; len++) {
+      if (obfuscated[len] == 0) break;
+    }
+    char[] plain = new char[len];
+    for (int i = 0; i < len; i++) {
+      plain[i] = (char)obfuscated[i];
+    }
+    return new String(plain);
+  }
+
+  static byte[] obfuscationKey = {23,82,107,6,35,78,88,7};
+}
diff --git a/java/src/com/tigervnc/rfb/VoidParameter.java b/java/src/com/tigervnc/rfb/VoidParameter.java
new file mode 100644
index 0000000..f41f4c8
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/VoidParameter.java
@@ -0,0 +1,41 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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;
+
+abstract public class VoidParameter {
+  public VoidParameter(String name_, String desc_) {
+    name = name_;
+    description = desc_;
+    next = Configuration.head;
+    Configuration.head = this;
+  }
+
+  final public String getName() { return name; }
+  final public String getDescription() { return description; }
+
+  abstract public boolean setParam(String value);
+  public boolean setParam() { return false; }
+  abstract public String getDefaultStr();
+  abstract public String getValueStr();
+  public boolean isBool() { return false; }
+
+  VoidParameter next;
+  protected String name;
+  protected String description;
+}
diff --git a/java/src/com/tigervnc/rfb/ZRLEDecoder.java b/java/src/com/tigervnc/rfb/ZRLEDecoder.java
new file mode 100644
index 0000000..4f740f9
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/ZRLEDecoder.java
@@ -0,0 +1,166 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  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;
+
+import com.tigervnc.rdr.*;
+
+public class ZRLEDecoder extends Decoder {
+
+  public ZRLEDecoder(CMsgReader reader_) {
+    reader = reader_;
+    zis = new ZlibInStream();
+  }
+
+  public void readRect(Rect r, CMsgHandler handler) {
+    InStream is = reader.getInStream();
+    int[] buf = reader.getImageBuf(64 * 64 * 4);
+    int bytesPerPixel = handler.cp.pf().bpp / 8;
+    boolean bigEndian = handler.cp.pf().bigEndian;
+
+    int length = is.readU32();
+    zis.setUnderlying(is, length);
+    Rect t = new Rect();
+
+    for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 64) {
+
+      t.br.y = Math.min(r.br.y, t.tl.y + 64);
+
+      for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 64) {
+
+        t.br.x = Math.min(r.br.x, t.tl.x + 64);
+
+        int mode = zis.readU8();
+        boolean rle = (mode & 128) != 0;
+        int palSize = mode & 127;
+        int[] palette = new int[128];
+
+        if (bytesPerPixel > 1) {
+          zis.readPixels(palette, palSize, 3, bigEndian);
+        } else {
+          for (int i = 0; i < palSize; i++) {
+            palette[i] = zis.readPixel(bytesPerPixel, bigEndian);
+          }
+        }
+        
+        if (palSize == 1) {
+          int pix = palette[0];
+          handler.fillRect(t, pix);
+          continue;
+        }
+
+        if (!rle) {
+          if (palSize == 0) {
+
+            // raw
+
+            if (bytesPerPixel > 1) {
+              zis.readPixels(buf, t.area(), 3, bigEndian);
+            } else {
+              zis.readPixels(buf, t.area(), bytesPerPixel, bigEndian);
+            }
+
+          } else {
+
+            // packed pixels
+            int bppp = ((palSize > 16) ? 8 :
+                        ((palSize > 4) ? 4 : ((palSize > 2) ? 2 : 1)));
+
+            int ptr = 0;
+
+            for (int i = 0; i < t.height(); i++) {
+              int eol = ptr + t.width();
+              int b = 0;
+              int nbits = 0;
+
+              while (ptr < eol) {
+                if (nbits == 0) {
+                  b = zis.readU8();
+                  nbits = 8;
+                }
+                nbits -= bppp;
+                int index = (b >> nbits) & ((1 << bppp) - 1) & 127;
+                buf[ptr++] = palette[index];
+              }
+            }
+          }
+
+        } else {
+
+          if (palSize == 0) {
+
+            // plain RLE
+
+            int ptr = 0;
+            int end = ptr + t.area();
+            while (ptr < end) {
+              int pix = (bytesPerPixel > 1 ? zis.readPixel(3, bigEndian) : 
+                zis.readPixel(bytesPerPixel, bigEndian));
+              int len = 1;
+              int b;
+              do {
+                b = zis.readU8();
+                len += b;
+              } while (b == 255);
+
+              if (!(len <= end - ptr))
+                throw new Exception("ZRLEDecoder: assertion (len <= end - ptr)"
+                                    +" failed");
+
+              while (len-- > 0) buf[ptr++] = pix;
+            }
+          } else {
+
+            // palette RLE
+
+            int ptr = 0;
+            int end = ptr + t.area();
+            while (ptr < end) {
+              int index = zis.readU8();
+              int len = 1;
+              if ((index & 128) != 0) {
+                int b;
+                do {
+                  b = zis.readU8();
+                  len += b;
+                } while (b == 255);
+
+                if (!(len <= end - ptr))
+                  throw new Exception("ZRLEDecoder: assertion "
+                                      +"(len <= end - ptr) failed");
+              }
+
+              index &= 127;
+
+              int pix = palette[index];
+
+              while (len-- > 0) buf[ptr++] = pix;
+            }
+          }
+        }
+
+        handler.imageRect(t, buf);
+      }
+    }
+
+    zis.reset();
+  }
+
+  CMsgReader reader;
+  ZlibInStream zis;
+}
diff --git a/java/src/com/tigervnc/rfb/screenTypes.java b/java/src/com/tigervnc/rfb/screenTypes.java
new file mode 100644
index 0000000..d46741c
--- /dev/null
+++ b/java/src/com/tigervnc/rfb/screenTypes.java
@@ -0,0 +1,36 @@
+/* Copyright 2009 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 screenTypes {
+
+  // Reasons
+  public static final int reasonServer = 0;
+  public static final int reasonClient = 1;
+  public static final int reasonOtherClient = 2;
+
+  // Result codes
+  public static final int resultSuccess = 0;
+  public static final int resultProhibited = 1;
+  public static final int resultNoResources = 2;
+  public static final int resultInvalid = 3;
+
+  public static final int resultUnsolicited = 0xffff; // internal code used for server changes
+
+}