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/decoder/CoRREDecoder.java b/java/src/com/tigervnc/decoder/CoRREDecoder.java
deleted file mode 100644
index dd50925..0000000
--- a/java/src/com/tigervnc/decoder/CoRREDecoder.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package com.tigervnc.decoder;
-
-import com.tigervnc.vncviewer.RfbInputStream;
-import java.awt.Graphics;
-import java.awt.Color;
-import java.io.IOException;
-
-//
-// Class that used for decoding CoRRE encoded data.
-//
-
-public class CoRREDecoder extends RawDecoder {
-
-  final static int EncodingCoRRE = 4;
-
-  public CoRREDecoder(Graphics g, RfbInputStream is) {
-    super(g, is);
-  }
-
-  public CoRREDecoder(Graphics g, RfbInputStream is, int frameBufferW,
-                      int frameBufferH) {
-    super(g, is, frameBufferW, frameBufferH);
-  }
-
-  //
-  // Override handleRect method to decode CoRRE encoded data insted of
-  // raw pixel data.
-  //
-
-  public void handleRect(int x, int y, int w, int h) throws IOException {
-
-    //
-    // Write encoding ID to record output stream
-    //
-
-    if (dos != null) {
-      dos.writeInt(CoRREDecoder.EncodingCoRRE);
-    }
-
-    int nSubrects = rfbis.readU32();
-
-    byte[] bg_buf = new byte[bytesPerPixel];
-    rfbis.readFully(bg_buf);
-    Color pixel;
-    if (bytesPerPixel == 1) {
-      pixel = getColor256()[bg_buf[0] & 0xFF];
-    } else {
-      pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, bg_buf[0] & 0xFF);
-    }
-    graphics.setColor(pixel);
-    graphics.fillRect(x, y, w, h);
-
-    byte[] buf = new byte[nSubrects * (bytesPerPixel + 4)];
-    rfbis.readFully(buf);
-
-    //
-    // Save decoded data to data output stream
-    //
-
-    if (dos != null) {
-      dos.writeInt(nSubrects);
-      dos.write(bg_buf);
-      dos.write(buf);
-    }
-
-    int sx, sy, sw, sh;
-    int i = 0;
-
-    for (int j = 0; j < nSubrects; j++) {
-      if (bytesPerPixel == 1) {
-        pixel = getColor256()[buf[i++] & 0xFF];
-      } else {
-        pixel = new Color(buf[i+2] & 0xFF, buf[i+1] & 0xFF, buf[i] & 0xFF);
-        i += 4;
-      }
-      sx = x + (buf[i++] & 0xFF);
-      sy = y + (buf[i++] & 0xFF);
-      sw = buf[i++] & 0xFF;
-      sh = buf[i++] & 0xFF;
-
-      graphics.setColor(pixel);
-      graphics.fillRect(sx, sy, sw, sh);
-    }
-  }
-}
diff --git a/java/src/com/tigervnc/decoder/CopyRectDecoder.java b/java/src/com/tigervnc/decoder/CopyRectDecoder.java
deleted file mode 100644
index c3f1c8c..0000000
--- a/java/src/com/tigervnc/decoder/CopyRectDecoder.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.tigervnc.decoder;
-
-import com.tigervnc.vncviewer.RfbInputStream;
-import java.awt.Graphics;
-import java.io.IOException;
-
-//
-// Class that used for decoding CopyRect encoded data.
-//
-
-public class CopyRectDecoder extends RawDecoder {
-
-  final static int EncodingCopyRect = 1;
-
-  public CopyRectDecoder(Graphics g, RfbInputStream is) {
-    super(g, is);
-  }
-
-  public CopyRectDecoder(Graphics g, RfbInputStream is, int frameBufferW,
-                         int frameBufferH) {
-    super(g, is, frameBufferW, frameBufferH);
-  }
-
-  //
-  // Override handleRect method handle CopyRect
-  //
-
-  public void handleRect(int x, int y, int w, int h) throws IOException {
-
-    //
-    // Write encoding ID to record output stream
-    //
-
-    if (dos != null) {
-      dos.writeInt(CopyRectDecoder.EncodingCopyRect);
-    }
-
-    int copyRectSrcX = rfbis.readU16();
-    int copyRectSrcY = rfbis.readU16();
-
-    // If the session is being recorded:
-    if (dos != null) {
-      dos.writeShort(copyRectSrcX);
-      dos.writeShort(copyRectSrcY);
-    }
-
-    graphics.copyArea(copyRectSrcX, copyRectSrcY, w, h,
-                      x - copyRectSrcX, y - copyRectSrcY);
-  }
-}
diff --git a/java/src/com/tigervnc/decoder/HextileDecoder.java b/java/src/com/tigervnc/decoder/HextileDecoder.java
deleted file mode 100644
index 7052ff4..0000000
--- a/java/src/com/tigervnc/decoder/HextileDecoder.java
+++ /dev/null
@@ -1,228 +0,0 @@
-package com.tigervnc.decoder;
-
-import com.tigervnc.decoder.common.Repaintable;
-import com.tigervnc.vncviewer.RfbInputStream;
-import java.awt.Color;
-import java.awt.Graphics;
-import java.io.IOException;
-
-//
-// Class that used for decoding hextile encoded data.
-//
-
-public class HextileDecoder extends RawDecoder {
-
-  final static int EncodingHextile = 5;
-
-  // Contstants used in the Hextile decoder
-  final static int
-    HextileRaw                 = 1,
-    HextileBackgroundSpecified = 2,
-    HextileForegroundSpecified = 4,
-    HextileAnySubrects         = 8,
-    HextileSubrectsColoured    = 16;
-
-  public HextileDecoder(Graphics g, RfbInputStream is) {
-    super(g, is);
-  }
-
-  public HextileDecoder(Graphics g, RfbInputStream is, int frameBufferW,
-                        int frameBufferH) {
-    super(g, is, frameBufferW, frameBufferH);
-  }
-
-  //
-  // Set private members methods
-  //
-
-  public void setRepainableControl(Repaintable r) {
-    repainableControl = r;
-  }
-
-  //
-  // Override handleRect method to decode Hextile encoded data insted of
-  // raw pixel data.
-  //
-
-  public void handleRect(int x, int y, int w, int h) throws IOException,
-                                                            Exception {
-
-    //
-    // Write encoding ID to record output stream
-    //
-
-    if (dos != null) {
-      dos.writeInt(HextileDecoder.EncodingHextile);
-    }
-
-    hextile_bg = new Color(0);
-    hextile_fg = new Color(0);
-
-    for (int ty = y; ty < y + h; ty += 16) {
-      int th = 16;
-      if (y + h - ty < 16)
-        th = y + h - ty;
-
-      for (int tx = x; tx < x + w; tx += 16) {
-        int tw = 16;
-        if (x + w - tx < 16)
-          tw = x + w - tx;
-
-        handleHextileSubrect(tx, ty, tw, th);
-      }
-      if (repainableControl != null)
-        repainableControl.scheduleRepaint(x, y, w, h);
-    }
-    if (repainableControl != null)
-      repainableControl.scheduleRepaint(x, y, w, h);
-  }
-
-  //
-  // Handle one tile in the Hextile-encoded data.
-  //
-
-  private void handleHextileSubrect(int tx, int ty, int tw, int th)
-    throws IOException, Exception {
-
-    int subencoding = rfbis.readU8();
-
-    //
-    // Save decoded data to data output stream
-    //
-
-    if (dos != null) {
-      dos.writeByte((byte)subencoding);
-    }
-
-    // Is it a raw-encoded sub-rectangle?
-    if ((subencoding & HextileRaw) != 0) {
-      //
-      // Disable encoding id writting to record stream
-      // in super (RawDecoder) class, cause we write subencoding ID
-      // in this class (see code above).
-      //
-
-      super.enableEncodingRecordWritting(false);
-      super.handleRect(tx, ty, tw, th);
-      super.handleUpdatedPixels(tx, ty, tw, th);
-      super.enableEncodingRecordWritting(true);
-      return;
-    }
-
-    // Read and draw the background if specified.
-    byte[] cbuf = new byte[bytesPerPixel];
-    if ((subencoding & HextileBackgroundSpecified) != 0) {
-      rfbis.readFully(cbuf);
-      if (bytesPerPixel == 1) {
-        hextile_bg = getColor256()[cbuf[0] & 0xFF];
-      } else {
-        hextile_bg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, cbuf[0] & 0xFF);
-      }
-
-      //
-      // Save decoded data to data output stream
-      //
-
-      if (dos != null) {
-        dos.write(cbuf);
-      }
-    }
-    graphics.setColor(hextile_bg);
-    graphics.fillRect(tx, ty, tw, th);
-
-    // Read the foreground color if specified.
-    if ((subencoding & HextileForegroundSpecified) != 0) {
-      rfbis.readFully(cbuf);
-      if (bytesPerPixel == 1) {
-        hextile_fg = getColor256()[cbuf[0] & 0xFF];
-      } else {
-        hextile_fg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, cbuf[0] & 0xFF);
-      }
-
-      //
-      // Save decoded data to data output stream
-      //
-
-      if (dos != null) {
-        dos.write(cbuf);
-      }
-    }
-
-    // Done with this tile if there is no sub-rectangles.
-    if ((subencoding & HextileAnySubrects) == 0)
-      return;
-
-    int nSubrects = rfbis.readU8();
-    int bufsize = nSubrects * 2;
-    if ((subencoding & HextileSubrectsColoured) != 0) {
-      bufsize += nSubrects * bytesPerPixel;
-    }
-    byte[] buf = new byte[bufsize];
-    rfbis.readFully(buf);
-
-    //
-    // Save decoded data to data output stream
-    //
-
-    if (dos != null) {
-      dos.writeByte((byte)nSubrects);
-      dos.write(buf);
-    }
-
-    int b1, b2, sx, sy, sw, sh;
-    int i = 0;
-
-    if ((subencoding & HextileSubrectsColoured) == 0) {
-
-      // Sub-rectangles are all of the same color.
-      graphics.setColor(hextile_fg);
-      for (int j = 0; j < nSubrects; j++) {
-        b1 = buf[i++] & 0xFF;
-        b2 = buf[i++] & 0xFF;
-        sx = tx + (b1 >> 4);
-        sy = ty + (b1 & 0xf);
-        sw = (b2 >> 4) + 1;
-        sh = (b2 & 0xf) + 1;
-        graphics.fillRect(sx, sy, sw, sh);
-      }
-    } else if (bytesPerPixel == 1) {
-
-      // BGR233 (8-bit color) version for colored sub-rectangles.
-      for (int j = 0; j < nSubrects; j++) {
-        hextile_fg = getColor256()[buf[i++] & 0xFF];
-        b1 = buf[i++] & 0xFF;
-        b2 = buf[i++] & 0xFF;
-        sx = tx + (b1 >> 4);
-        sy = ty + (b1 & 0xf);
-        sw = (b2 >> 4) + 1;
-        sh = (b2 & 0xf) + 1;
-        graphics.setColor(hextile_fg);
-        graphics.fillRect(sx, sy, sw, sh);
-      }
-
-    } else {
-
-      // Full-color (24-bit) version for colored sub-rectangles.
-      for (int j = 0; j < nSubrects; j++) {
-        hextile_fg = new Color(buf[i+2] & 0xFF,
-                               buf[i+1] & 0xFF,
-                               buf[i] & 0xFF);
-        i += 4;
-        b1 = buf[i++] & 0xFF;
-        b2 = buf[i++] & 0xFF;
-        sx = tx + (b1 >> 4);
-        sy = ty + (b1 & 0xf);
-        sw = (b2 >> 4) + 1;
-        sh = (b2 & 0xf) + 1;
-        graphics.setColor(hextile_fg);
-        graphics.fillRect(sx, sy, sw, sh);
-      }
-
-    }
-  }
-
-  // These colors should be kept between handleHextileSubrect() calls.
-  private Color hextile_bg, hextile_fg;
-  // Repaitable object
-  private Repaintable repainableControl = null;
-}
diff --git a/java/src/com/tigervnc/decoder/RREDecoder.java b/java/src/com/tigervnc/decoder/RREDecoder.java
deleted file mode 100644
index f6a67ae..0000000
--- a/java/src/com/tigervnc/decoder/RREDecoder.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package com.tigervnc.decoder;
-
-import com.tigervnc.vncviewer.RfbInputStream;
-import java.awt.Graphics;
-import java.awt.Color;
-import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
-import java.io.IOException;
-
-//
-// Class that used for decoding RRE encoded data.
-//
-
-public class RREDecoder extends RawDecoder {
-
-  final static int EncodingRRE = 2;
-
-  public RREDecoder(Graphics g, RfbInputStream is) {
-    super(g, is);
-  }
-
-  public RREDecoder(Graphics g, RfbInputStream is, int frameBufferW,
-                    int frameBufferH) {
-    super(g, is, frameBufferW, frameBufferH);
-  }
-
-  //
-  // Override handleRect method to decode RRE encoded data insted of
-  // raw pixel data.
-  //
-
-  public void handleRect(int x, int y, int w, int h) throws IOException {
-
-    //
-    // Write encoding ID to record output stream
-    //
-
-    if (dos != null) {
-      dos.writeInt(RREDecoder.EncodingRRE);
-    }
-
-    int nSubrects = rfbis.readU32();
-    byte[] bg_buf = new byte[bytesPerPixel];
-    rfbis.readFully(bg_buf);
-    Color pixel;
-    if (bytesPerPixel == 1) {
-      pixel = getColor256()[bg_buf[0] & 0xFF];
-    } else {
-      pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, bg_buf[0] & 0xFF);
-    }
-    graphics.setColor(pixel);
-    graphics.fillRect(x, y, w, h);
-    byte[] buf = new byte[nSubrects * (bytesPerPixel + 8)];
-    rfbis.readFully(buf);
-    DataInputStream ds = new DataInputStream(new ByteArrayInputStream(buf));
-
-    //
-    // Save decoded data to data output stream
-    //
-    if (dos != null) {
-      dos.writeInt(nSubrects);
-      dos.write(bg_buf);
-      dos.write(buf);
-    }
-
-    int sx, sy, sw, sh;
-    for (int j = 0; j < nSubrects; j++) {
-      if (bytesPerPixel == 1) {
-        pixel = getColor256()[ds.readUnsignedByte()];
-      } else {
-        ds.skip(4);
-        pixel = new Color(buf[j*12+2] & 0xFF,
-                          buf[j*12+1] & 0xFF,
-                          buf[j*12]   & 0xFF);
-      }
-      sx = x + ds.readUnsignedShort();
-      sy = y + ds.readUnsignedShort();
-      sw = ds.readUnsignedShort();
-      sh = ds.readUnsignedShort();
-
-      graphics.setColor(pixel);
-      graphics.fillRect(sx, sy, sw, sh);
-    }
-  }
-}
diff --git a/java/src/com/tigervnc/decoder/RawDecoder.java b/java/src/com/tigervnc/decoder/RawDecoder.java
deleted file mode 100644
index 478a668..0000000
--- a/java/src/com/tigervnc/decoder/RawDecoder.java
+++ /dev/null
@@ -1,223 +0,0 @@
-package com.tigervnc.decoder;
-
-import com.tigervnc.vncviewer.RfbInputStream;
-import java.io.IOException;
-import java.io.DataOutput;
-import java.awt.Graphics;
-import java.awt.Image;
-import java.awt.image.ColorModel;
-import java.awt.image.DirectColorModel;
-import java.awt.image.MemoryImageSource;
-import java.awt.Color;
-import java.awt.Toolkit;
-
-//
-// This is base decoder class.
-// Other classes will be childs of RawDecoder.
-//
-
-public class RawDecoder {
-  final static int EncodingRaw = 0;
-
-  public RawDecoder(Graphics g, RfbInputStream is) {
-    setGraphics(g);
-    setRfbInputStream(is);
-  }
-
-  public RawDecoder(Graphics g, RfbInputStream is, int frameBufferW,
-                    int frameBufferH) {
-    setGraphics(g);
-    setRfbInputStream(is);
-    setFrameBufferSize(frameBufferW, frameBufferH);
-    // FIXME: cm24 created in getColorModel24.
-    // Remove if no bugs
-    cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
-  }
-
-  //
-  // Set methods to set value of non-static protected members of class
-  //
-
-  public void setRfbInputStream(RfbInputStream is) {
-    rfbis = is;
-  }
-
-  public void setGraphics(Graphics g) {
-    graphics = g;
-  }
-
-  public void setBPP(int bpp) {
-    bytesPerPixel = bpp;
-  }
-
-  public void setFrameBufferSize(int w, int h) {
-    framebufferWidth = w;
-    framebufferHeight = h;
-  }
-
-  //
-  // FIXME: Rename this method after we don't need RecordInterface
-  // in RawDecoder class to record session
-  //
-
-  public void setDataOutputStream(DataOutput os) {
-    dos = os;
-  }
-
-  //
-  // Decodes Raw Pixels data and draw it into graphics
-  //
-
-  public void handleRect(int x, int y, int w, int h) throws IOException, Exception {
-
-    //
-    // Write encoding ID to record output stream
-    //
-
-    if ((dos != null) && (enableEncodingRecordWritting)) {
-      dos.writeInt(RawDecoder.EncodingRaw);
-    }
-
-    if (bytesPerPixel == 1) {
-      for (int dy = y; dy < y + h; dy++) {
-        if (pixels8 != null) {
-          rfbis.readFully(pixels8, dy * framebufferWidth + x, w);
-        }
-        //
-        // Save decoded data to record output stream
-        //
-        if (dos != null) {
-          dos.write(pixels8, dy * framebufferWidth + x, w);
-        }
-      }
-    } else {
-      byte[] buf = new byte[w * 4];
-      int i, offset;
-      for (int dy = y; dy < y + h; dy++) {
-        rfbis.readFully(buf);
-        //
-        // Save decoded data to record output stream
-        //
-        if (dos != null) {
-          dos.write(buf);
-        }
-        offset = dy * framebufferWidth + x;
-        if (pixels24 != null) {
-          for (i = 0; i < w; i++) {
-            pixels24[offset + i] =
-              (buf[i * 4 + 2] & 0xFF) << 16 |
-              (buf[i * 4 + 1] & 0xFF) << 8 |
-              (buf[i * 4] & 0xFF);
-          } //for
-        } // if
-      } // for
-    } // else
-    handleUpdatedPixels(x, y, w, h);
-  } // void
-
-  //
-  // Display newly updated area of pixels.
-  //
-
-  protected void handleUpdatedPixels(int x, int y, int w, int h) {
-    // Draw updated pixels of the off-screen image.
-    pixelsSource.newPixels(x, y, w, h);
-    graphics.setClip(x, y, w, h);
-    graphics.drawImage(rawPixelsImage, 0, 0, null);
-    graphics.setClip(0, 0, framebufferWidth, framebufferHeight);
-  }
-
-  //
-  // Updates pixels data.
-  // This method must be called when framebuffer is resized
-  // or BPP is changed.
-  //
-
-  public void update() {
-    // Images with raw pixels should be re-allocated on every change
-    // of geometry or pixel format.
-    int fbWidth = framebufferWidth;
-    int fbHeight = framebufferHeight;
-
-    if (bytesPerPixel == 1) {
-      pixels24 = null;
-      pixels8 = new byte[fbWidth * fbHeight];
-      pixelsSource = new MemoryImageSource(fbWidth, fbHeight, getColorModel8(),
-                                           pixels8, 0, fbWidth);
-    } else {
-      pixels8 = null;
-      pixels24 = new int[fbWidth * fbHeight];
-      pixelsSource =
-        new MemoryImageSource(fbWidth, fbHeight, cm24, pixels24, 0, fbWidth);
-    }
-    pixelsSource.setAnimated(true);
-    rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource);
-  }
-
-  //
-  // Private static members access methods
-  //
-
-  protected ColorModel getColorModel8() {
-    if (cm8 == null) {
-      cm8 = cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6));
-    }
-    return cm8;
-  }
-
-  protected ColorModel getColorModel24() {
-    if (cm24 == null) {
-      cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
-    }
-    return cm24;
-  }
-
-  protected Color[]getColor256() {
-    if (color256 == null) {
-      color256 = new Color[256];
-      for (int i = 0; i < 256; i++)
-        color256[i] = new Color(cm8.getRGB(i));
-    }
-    return color256;
-  }
-
-  //
-  // This method will be used by HextileDecoder to disable
-  // double writting encoding id to record stream.
-  //
-  // FIXME: Try to find better solution than this.
-  //
-
-  protected void enableEncodingRecordWritting(boolean enable) {
-    enableEncodingRecordWritting = enable;
-  }
-
-  //
-  // Unique data for every decoder (? maybe not ?)
-  //
-
-  protected int bytesPerPixel = 4;
-  protected int framebufferWidth = 0;
-  protected int framebufferHeight = 0;
-  protected RfbInputStream rfbis = null;
-  protected Graphics graphics = null;
-  protected DataOutput dos = null;
-  protected boolean enableEncodingRecordWritting = true;
-
-  //
-  // This data must be shared between decoders
-  //
-
-  protected static byte []pixels8 = null;
-  protected static int []pixels24 = null;
-  protected static MemoryImageSource pixelsSource = null;
-  protected static Image rawPixelsImage = null;
-
-  //
-  // Access to this static members only though protected methods
-  //
-
-  private static ColorModel cm8 = null;
-  private static ColorModel cm24 = null;
-  private static Color []color256 = null;
-}
diff --git a/java/src/com/tigervnc/decoder/TightDecoder.java b/java/src/com/tigervnc/decoder/TightDecoder.java
deleted file mode 100644
index 4e9969c..0000000
--- a/java/src/com/tigervnc/decoder/TightDecoder.java
+++ /dev/null
@@ -1,525 +0,0 @@
-package com.tigervnc.decoder;
-
-import com.tigervnc.decoder.common.Repaintable;
-import com.tigervnc.vncviewer.RfbInputStream;
-import java.awt.Graphics;
-import java.awt.Color;
-import java.awt.Image;
-import java.awt.Rectangle;
-import java.awt.Toolkit;
-import java.awt.image.ImageObserver;
-import java.io.IOException;
-import java.util.zip.Deflater;
-import java.util.zip.Inflater;
-
-//
-// Class that used for decoding Tight encoded data.
-//
-
-public class TightDecoder extends RawDecoder implements ImageObserver {
-
-  final static int EncodingTight = 7;
-
-  //
-  // Tight decoder constants
-  //
-
-  final static int TightExplicitFilter = 0x04;
-  final static int TightFill = 0x08;
-  final static int TightJpeg = 0x09;
-  final static int TightMaxSubencoding = 0x09;
-  final static int TightFilterCopy = 0x00;
-  final static int TightFilterPalette = 0x01;
-  final static int TightFilterGradient = 0x02;
-  final static int TightMinToCompress = 12;
-
-  // Tight encoder's data.
-  final static int tightZlibBufferSize = 512;
-
-  public TightDecoder(Graphics g, RfbInputStream is) {
-    super(g, is);
-    tightInflaters = new Inflater[4];
-  }
-
-  public TightDecoder(Graphics g, RfbInputStream is, int frameBufferW,
-                      int frameBufferH) {
-    super(g, is, frameBufferW, frameBufferH);
-    tightInflaters = new Inflater[4];
-  }
-
-  //
-  // Set and get methods for private TightDecoder
-  //
-
-  public void setRepainableControl(Repaintable r) {
-    repainatableControl = r;
-  }
-
-  //
-  // JPEG processing statistic methods
-  //
-
-  public long getNumJPEGRects() {
-    return statNumRectsTightJPEG;
-  }
-
-  public void setNumJPEGRects(int v) {
-    statNumRectsTightJPEG = v;
-  }
-
-  //
-  // Tight processing statistic methods
-  //
-
-  public long getNumTightRects() {
-    return statNumRectsTight;
-  }
-
-  public void setNumTightRects(int v) {
-    statNumRectsTight = v;
-  }
-
-  //
-  // Handle a Tight-encoded rectangle.
-  //
-
-  public void handleRect(int x, int y, int w, int h) throws Exception {
-
-    //
-    // Write encoding ID to record output stream
-    //
-
-    if (dos != null) {
-      dos.writeInt(TightDecoder.EncodingTight);
-    }
-
-    int comp_ctl = rfbis.readU8();
-
-    if (dos != null) {
-      // Tell the decoder to flush each of the four zlib streams.
-      dos.writeByte(comp_ctl | 0x0F);
-    }
-
-    // Flush zlib streams if we are told by the server to do so.
-    for (int stream_id = 0; stream_id < 4; stream_id++) {
-      if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) {
-        tightInflaters[stream_id] = null;
-      }
-      comp_ctl >>= 1;
-    }
-
-    // Check correctness of subencoding value.
-    if (comp_ctl > TightDecoder.TightMaxSubencoding) {
-      throw new Exception("Incorrect tight subencoding: " + comp_ctl);
-    }
-
-    // Handle solid-color rectangles.
-    if (comp_ctl == TightDecoder.TightFill) {
-
-      if (bytesPerPixel == 1) {
-        int idx = rfbis.readU8();
-        graphics.setColor(getColor256()[idx]);
-        if (dos != null) {
-          dos.writeByte(idx);
-        }
-      } else {
-        byte[] buf = new byte[3];
-        rfbis.readFully(buf);
-        if (dos != null) {
-          dos.write(buf);
-        }
-        Color bg = new Color(0xFF000000 | (buf[0] & 0xFF) << 16 |
-                            (buf[1] & 0xFF) << 8 | (buf[2] & 0xFF));
-        graphics.setColor(bg);
-      }
-      graphics.fillRect(x, y, w, h);
-      repainatableControl.scheduleRepaint(x, y, w, h);
-      return;
-
-    }
-
-    if (comp_ctl == TightDecoder.TightJpeg) {
-
-      statNumRectsTightJPEG++;
-
-      // Read JPEG data.
-      byte[] jpegData = new byte[rfbis.readCompactLen()];
-      rfbis.readFully(jpegData);
-      if (dos != null) {
-        recordCompactLen(jpegData.length);
-        dos.write(jpegData);
-      }
-
-      // Create an Image object from the JPEG data.
-      Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData);
-
-      // Remember the rectangle where the image should be drawn.
-      jpegRect = new Rectangle(x, y, w, h);
-
-      // Let the imageUpdate() method do the actual drawing, here just
-      // wait until the image is fully loaded and drawn.
-      synchronized(jpegRect) {
-        Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1, this);
-        try {
-          // Wait no longer than three seconds.
-          jpegRect.wait(3000);
-        } catch (InterruptedException e) {
-          throw new Exception("Interrupted while decoding JPEG image");
-        }
-      }
-
-      // Done, jpegRect is not needed any more.
-      jpegRect = null;
-      return;
-
-    } else {
-      statNumRectsTight++;
-    }
-
-    // Read filter id and parameters.
-    int numColors = 0, rowSize = w;
-    byte[] palette8 = new byte[2];
-    int[] palette24 = new int[256];
-    boolean useGradient = false;
-    if ((comp_ctl & TightDecoder.TightExplicitFilter) != 0) {
-      int filter_id = rfbis.readU8();
-      if (dos != null) {
-        dos.writeByte(filter_id);
-      }
-      if (filter_id == TightDecoder.TightFilterPalette) {
-        numColors = rfbis.readU8() + 1;
-        if (dos != null) {
-          dos.writeByte((numColors - 1));
-        }
-        if (bytesPerPixel == 1) {
-          if (numColors != 2) {
-            throw new Exception("Incorrect tight palette size: " + numColors);
-          }
-          rfbis.readFully(palette8);
-          if (dos != null) {
-            dos.write(palette8);
-          }
-        } else {
-          byte[] buf = new byte[numColors * 3];
-          rfbis.readFully(buf);
-          if (dos != null) {
-            dos.write(buf);
-          }
-          for (int i = 0; i < numColors; i++) {
-           palette24[i] = ((buf[i * 3] & 0xFF) << 16 |
-                           (buf[i * 3 + 1] & 0xFF) << 8 |
-                           (buf[i * 3 + 2] & 0xFF));
-          }
-        }
-        if (numColors == 2) {
-          rowSize = (w + 7) / 8;
-        }
-      } else if (filter_id == TightDecoder.TightFilterGradient) {
-        useGradient = true;
-      } else if (filter_id != TightDecoder.TightFilterCopy) {
-        throw new Exception("Incorrect tight filter id: " + filter_id);
-      }
-    }
-    if (numColors == 0 && bytesPerPixel == 4)
-      rowSize *= 3;
-
-    // Read, optionally uncompress and decode data.
-    int dataSize = h * rowSize;
-    if (dataSize < TightDecoder.TightMinToCompress) {
-      // Data size is small - not compressed with zlib.
-      if (numColors != 0) {
-        // Indexed colors.
-        byte[] indexedData = new byte[dataSize];
-        rfbis.readFully(indexedData);
-        if (dos != null) {
-          dos.write(indexedData);
-        }
-        if (numColors == 2) {
-          // Two colors.
-          if (bytesPerPixel == 1) {
-            decodeMonoData(x, y, w, h, indexedData, palette8);
-          } else {
-            decodeMonoData(x, y, w, h, indexedData, palette24);
-          }
-        } else {
-          // 3..255 colors (assuming bytesPixel == 4).
-          int i = 0;
-          for (int dy = y; dy < y + h; dy++) {
-            for (int dx = x; dx < x + w; dx++) {
-              pixels24[dy * framebufferWidth + dx] =
-                palette24[indexedData[i++] & 0xFF];
-            }
-          }
-        }
-      } else if (useGradient) {
-        // "Gradient"-processed data
-        byte[] buf = new byte[w * h * 3];
-        rfbis.readFully(buf);
-        if (dos != null) {
-          dos.write(buf);
-        }
-        decodeGradientData(x, y, w, h, buf);
-      } else {
-        // Raw truecolor data.
-        if (bytesPerPixel == 1) {
-          for (int dy = y; dy < y + h; dy++) {
-            rfbis.readFully(pixels8, dy * framebufferWidth + x, w);
-            if (dos != null) {
-              dos.write(pixels8, dy * framebufferWidth + x, w);
-            }
-          }
-        } else {
-          byte[] buf = new byte[w * 3];
-          int i, offset;
-          for (int dy = y; dy < y + h; dy++) {
-            rfbis.readFully(buf);
-            if (dos != null) {
-              dos.write(buf);
-            }
-            offset = dy * framebufferWidth + x;
-            for (i = 0; i < w; i++) {
-              pixels24[offset + i] =
-                (buf[i * 3] & 0xFF) << 16 |
-                (buf[i * 3 + 1] & 0xFF) << 8 |
-                (buf[i * 3 + 2] & 0xFF);
-            }
-          }
-        }
-      }
-    } else {
-      // Data was compressed with zlib.
-      int zlibDataLen = rfbis.readCompactLen();
-      byte[] zlibData = new byte[zlibDataLen];
-      rfbis.readFully(zlibData);
-      int stream_id = comp_ctl & 0x03;
-      if (tightInflaters[stream_id] == null) {
-        tightInflaters[stream_id] = new Inflater();
-      }
-      Inflater myInflater = tightInflaters[stream_id];
-      myInflater.setInput(zlibData);
-      byte[] buf = new byte[dataSize];
-      myInflater.inflate(buf);
-      if (dos != null) {
-        recordCompressedData(buf);
-      }
-
-      if (numColors != 0) {
-        // Indexed colors.
-        if (numColors == 2) {
-          // Two colors.
-          if (bytesPerPixel == 1) {
-            decodeMonoData(x, y, w, h, buf, palette8);
-          } else {
-            decodeMonoData(x, y, w, h, buf, palette24);
-          }
-        } else {
-          // More than two colors (assuming bytesPixel == 4).
-          int i = 0;
-          for (int dy = y; dy < y + h; dy++) {
-            for (int dx = x; dx < x + w; dx++) {
-              pixels24[dy * framebufferWidth + dx] =
-                palette24[buf[i++] & 0xFF];
-            }
-          }
-        }
-      } else if (useGradient) {
-        // Compressed "Gradient"-filtered data (assuming bytesPixel == 4).
-        decodeGradientData(x, y, w, h, buf);
-      } else {
-        // Compressed truecolor data.
-        if (bytesPerPixel == 1) {
-          int destOffset = y * framebufferWidth + x;
-          for (int dy = 0; dy < h; dy++) {
-            System.arraycopy(buf, dy * w, pixels8, destOffset, w);
-            destOffset += framebufferWidth;
-          }
-        } else {
-          int srcOffset = 0;
-          int destOffset, i;
-          for (int dy = 0; dy < h; dy++) {
-            myInflater.inflate(buf);
-            destOffset = (y + dy) * framebufferWidth + x;
-            for (i = 0; i < w; i++) {
-              RawDecoder.pixels24[destOffset + i] =
-                (buf[srcOffset] & 0xFF) << 16 |
-                (buf[srcOffset + 1] & 0xFF) << 8 |
-                (buf[srcOffset + 2] & 0xFF);
-              srcOffset += 3;
-            }
-          }
-        }
-      }
-    }
-    handleUpdatedPixels(x, y, w, h);
-  }
-
-  //
-  // Decode 1bpp-encoded bi-color rectangle (8-bit and 24-bit versions).
-  //
-
-  private void decodeMonoData(int x, int y, int w, int h, byte[] src, byte[] palette) {
-
-    int dx, dy, n;
-    int i = y * framebufferWidth + x;
-    int rowBytes = (w + 7) / 8;
-    byte b;
-
-    for (dy = 0; dy < h; dy++) {
-      for (dx = 0; dx < w / 8; dx++) {
-        b = src[dy*rowBytes+dx];
-        for (n = 7; n >= 0; n--)
-          pixels8[i++] = palette[b >> n & 1];
-      }
-      for (n = 7; n >= 8 - w % 8; n--) {
-        pixels8[i++] = palette[src[dy*rowBytes+dx] >> n & 1];
-      }
-      i += (framebufferWidth - w);
-    }
-  }
-
-  private void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette) {
-
-    int dx, dy, n;
-    int i = y * framebufferWidth + x;
-    int rowBytes = (w + 7) / 8;
-    byte b;
-
-    for (dy = 0; dy < h; dy++) {
-      for (dx = 0; dx < w / 8; dx++) {
-        b = src[dy*rowBytes+dx];
-        for (n = 7; n >= 0; n--)
-          pixels24[i++] = palette[b >> n & 1];
-      }
-      for (n = 7; n >= 8 - w % 8; n--) {
-        pixels24[i++] = palette[src[dy*rowBytes+dx] >> n & 1];
-      }
-      i += (framebufferWidth - w);
-    }
-  }
-
-  //
-  // Decode data processed with the "Gradient" filter.
-  //
-
-  private void decodeGradientData (int x, int y, int w, int h, byte[] buf) {
-
-    int dx, dy, c;
-    byte[] prevRow = new byte[w * 3];
-    byte[] thisRow = new byte[w * 3];
-    byte[] pix = new byte[3];
-    int[] est = new int[3];
-
-    int offset = y * framebufferWidth + x;
-
-    for (dy = 0; dy < h; dy++) {
-
-      /* First pixel in a row */
-      for (c = 0; c < 3; c++) {
-        pix[c] = (byte)(prevRow[c] + buf[dy * w * 3 + c]);
-        thisRow[c] = pix[c];
-      }
-      pixels24[offset++] =
-         (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
-
-      /* Remaining pixels of a row */
-      for (dx = 1; dx < w; dx++) {
-        for (c = 0; c < 3; c++) {
-          est[c] = ((prevRow[dx * 3 + c] & 0xFF) + (pix[c] & 0xFF) -
-                    (prevRow[(dx-1) * 3 + c] & 0xFF));
-          if (est[c] > 0xFF) {
-            est[c] = 0xFF;
-          } else if (est[c] < 0x00) {
-            est[c] = 0x00;
-          }
-          pix[c] = (byte)(est[c] + buf[(dy * w + dx) * 3 + c]);
-          thisRow[dx * 3 + c] = pix[c];
-        }
-        pixels24[offset++] =
-          (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
-      }
-
-      System.arraycopy(thisRow, 0, prevRow, 0, w * 3);
-      offset += (framebufferWidth - w);
-    }
-  }
-
-  //
-  // Override the ImageObserver interface method to handle drawing of
-  // JPEG-encoded data.
-  //
-
-  public boolean imageUpdate(Image img, int infoflags,
-                             int x, int y, int width, int height) {
-    if ((infoflags & (ALLBITS | ABORT)) == 0) {
-      return true; // We need more image data.
-    } else {
-      // If the whole image is available, draw it now.
-      if ((infoflags & ALLBITS) != 0) {
-        if (jpegRect != null) {
-          synchronized(jpegRect) {
-            graphics.drawImage(img, jpegRect.x, jpegRect.y, null);
-            repainatableControl.scheduleRepaint(jpegRect.x, jpegRect.y,
-                                                jpegRect.width, jpegRect.height);
-            jpegRect.notify();
-          }
-        }
-      }
-      return false; // All image data was processed.
-    }
-  }
-
-  //
-  // Write an integer in compact representation (1..3 bytes) into the
-  // recorded session file.
-  //
-
-  void recordCompactLen(int len) throws IOException {
-    byte[] buf = new byte[3];
-    int bytes = 0;
-    buf[bytes++] = (byte)(len & 0x7F);
-    if (len > 0x7F) {
-      buf[bytes-1] |= 0x80;
-      buf[bytes++] = (byte)(len >> 7 & 0x7F);
-      if (len > 0x3FFF) {
-	buf[bytes-1] |= 0x80;
-	buf[bytes++] = (byte)(len >> 14 & 0xFF);
-      }
-    }
-    if (dos != null) dos.write(buf, 0, bytes);
-  }
-
-  //
-  // Compress and write the data into the recorded session file.
-  //
-
-  void recordCompressedData(byte[] data, int off, int len) throws IOException {
-    Deflater deflater = new Deflater();
-    deflater.setInput(data, off, len);
-    int bufSize = len + len / 100 + 12;
-    byte[] buf = new byte[bufSize];
-    deflater.finish();
-    int compressedSize = deflater.deflate(buf);
-    recordCompactLen(compressedSize);
-    if (dos != null) dos.write(buf, 0, compressedSize);
-  }
-
-  void recordCompressedData(byte[] data) throws IOException {
-    recordCompressedData(data, 0, data.length);
-  }
-
-  //
-  // Private members
-  //
-
-  private Inflater[] tightInflaters;
-  // Since JPEG images are loaded asynchronously, we have to remember
-  // their position in the framebuffer. Also, this jpegRect object is
-  // used for synchronization between the rfbThread and a JVM's thread
-  // which decodes and loads JPEG images.
-  private Rectangle jpegRect;
-  private Repaintable repainatableControl = null;
-  // Jpeg decoding statistics
-  private long statNumRectsTightJPEG = 0;
-  // Tight decoding statistics
-  private long statNumRectsTight = 0;
-}
diff --git a/java/src/com/tigervnc/decoder/ZRLEDecoder.java b/java/src/com/tigervnc/decoder/ZRLEDecoder.java
deleted file mode 100644
index 91a9f9d..0000000
--- a/java/src/com/tigervnc/decoder/ZRLEDecoder.java
+++ /dev/null
@@ -1,310 +0,0 @@
-package com.tigervnc.decoder;
-
-import com.tigervnc.vncviewer.InStream;
-import com.tigervnc.vncviewer.RfbInputStream;
-import com.tigervnc.vncviewer.ZlibInStream;
-import java.awt.Graphics;
-import com.tigervnc.vncviewer.MemInStream;
-import java.awt.Color;
-import java.awt.Toolkit;
-import java.awt.image.MemoryImageSource;
-import java.io.IOException;
-
-//
-// Class that used for decoding ZRLE encoded data.
-//
-
-public class ZRLEDecoder extends RawDecoder {
-
-  final static int EncodingZRLE = 16;
-
-  public ZRLEDecoder(Graphics g, RfbInputStream is) {
-    super(g, is);
-  }
-
-  public ZRLEDecoder(Graphics g, RfbInputStream is, int frameBufferW,
-                     int frameBufferH) {
-    super(g, is, frameBufferW, frameBufferH);
-  }
-
-  //
-  // Handle a ZRLE-encoded rectangle.
-  //
-  // FIXME: Currently, session recording is not fully supported for ZRLE.
-  //
-
-  public void handleRect(int x, int y, int w, int h) throws IOException, Exception {
-
-    //
-    // Write encoding ID to record output stream
-    //
-
-    if (dos != null) {
-      dos.writeInt(ZRLEDecoder.EncodingZRLE);
-    }
-
-    if (zrleInStream == null)
-      zrleInStream = new ZlibInStream();
-
-    int nBytes = rfbis.readU32();
-    if (nBytes > 64 * 1024 * 1024)
-      throw new Exception("ZRLE decoder: illegal compressed data size");
-
-    if (zrleBuf == null || zrleBufLen < nBytes) {
-      zrleBufLen = nBytes + 4096;
-      zrleBuf = new byte[zrleBufLen];
-    }
-
-    // FIXME: Do not wait for all the data before decompression.
-    rfbis.readFully(zrleBuf, 0, nBytes);
-
-    //
-    // Override handleRect method to decode RRE encoded data insted of
-    // raw pixel data.
-    //
-
-    if (dos != null) {
-      if (!zrleRecWarningShown) {
-        System.out.println("Warning: ZRLE session can be recorded" +
-                           " only from the beginning");
-        System.out.println("Warning: Recorded file may be corrupted");
-        zrleRecWarningShown = true;
-      }
-    }
-
-    zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes);
-
-    for (int ty = y; ty < y+h; ty += 64) {
-
-      int th = Math.min(y+h-ty, 64);
-
-      for (int tx = x; tx < x+w; tx += 64) {
-
-        int tw = Math.min(x+w-tx, 64);
-
-        int mode = zrleInStream.readU8();
-        boolean rle = (mode & 128) != 0;
-        int palSize = mode & 127;
-        int[] palette = new int[128];
-
-        readZrlePalette(palette, palSize);
-
-        if (palSize == 1) {
-          int pix = palette[0];
-          Color c = (bytesPerPixel == 1) ?
-            getColor256()[pix] : new Color(0xFF000000 | pix);
-          graphics.setColor(c);
-          graphics.fillRect(tx, ty, tw, th);
-          continue;
-        }
-
-        if (!rle) {
-          if (palSize == 0) {
-            readZrleRawPixels(tw, th);
-          } else {
-            readZrlePackedPixels(tw, th, palette, palSize);
-          }
-        } else {
-          if (palSize == 0) {
-            readZrlePlainRLEPixels(tw, th);
-          } else {
-            readZrlePackedRLEPixels(tw, th, palette);
-          }
-        }
-        handleUpdatedZrleTile(tx, ty, tw, th);
-      }
-    }
-    zrleInStream.reset();
-  }
-
-  //
-  // Override update() method cause we have own data that
-  // must be updated when framebuffer is resized or BPP is changed
-  //
-
-  public void update() {
-    // Images with raw pixels should be re-allocated on every change
-    // of geometry or pixel format.
-    int fbWidth = framebufferWidth;
-    int fbHeight = framebufferHeight;
-
-    if (bytesPerPixel == 1) {
-      RawDecoder.pixels24 = null;
-      RawDecoder.pixels8 = new byte[fbWidth * fbHeight];
-      RawDecoder.pixelsSource = new MemoryImageSource(fbWidth, fbHeight, getColorModel8(), pixels8, 0, fbWidth);
-      zrleTilePixels24 = null;
-      zrleTilePixels8 = new byte[64 * 64];
-    } else {
-      RawDecoder.pixels8 = null;
-      RawDecoder.pixels24 = new int[fbWidth * fbHeight];
-      RawDecoder.pixelsSource =
-        new MemoryImageSource(fbWidth, fbHeight, getColorModel24(), pixels24, 0, fbWidth);
-      zrleTilePixels8 = null;
-      zrleTilePixels24 = new int[64 * 64];
-    }
-    RawDecoder.pixelsSource.setAnimated(true);
-    RawDecoder.rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource);
-  }
-
-  //
-  // Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update.
-  //
-
-  private void handleUpdatedZrleTile(int x, int y, int w, int h) {
-    Object src, dst;
-    if (bytesPerPixel == 1) {
-      src = zrleTilePixels8; dst = pixels8;
-    } else {
-      src = zrleTilePixels24; dst = pixels24;
-    }
-    int offsetSrc = 0;
-    int offsetDst = (y * framebufferWidth + x);
-    for (int j = 0; j < h; j++) {
-      System.arraycopy(src, offsetSrc, dst, offsetDst, w);
-      offsetSrc += w;
-      offsetDst += framebufferWidth;
-    }
-    handleUpdatedPixels(x, y, w, h);
-  }
-
-  //
-  // Private methods for reading ZRLE data
-  //
-
-  private int readPixel(InStream is) throws Exception {
-    int pix;
-    if (bytesPerPixel == 1) {
-      pix = is.readU8();
-    } else {
-      int p1 = is.readU8();
-      int p2 = is.readU8();
-      int p3 = is.readU8();
-      pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF);
-    }
-    return pix;
-  }
-
-  private void readPixels(InStream is, int[] dst, int count) throws Exception {
-    if (bytesPerPixel == 1) {
-      byte[] buf = new byte[count];
-      is.readBytes(buf, 0, count);
-      for (int i = 0; i < count; i++) {
-        dst[i] = (int)buf[i] & 0xFF;
-      }
-    } else {
-      byte[] buf = new byte[count * 3];
-      is.readBytes(buf, 0, count * 3);
-      for (int i = 0; i < count; i++) {
-        dst[i] = ((buf[i*3+2] & 0xFF) << 16 |
-                  (buf[i*3+1] & 0xFF) << 8 |
-                  (buf[i*3] & 0xFF));
-      }
-    }
-  }
-
-  private void readZrlePalette(int[] palette, int palSize) throws Exception {
-    readPixels(zrleInStream, palette, palSize);
-  }
-
-  private void readZrleRawPixels(int tw, int th) throws Exception {
-    if (bytesPerPixel == 1) {
-      zrleInStream.readBytes(zrleTilePixels8, 0, tw * th);
-    } else {
-      readPixels(zrleInStream, zrleTilePixels24, tw * th); ///
-    }
-  }
-
-  private void readZrlePackedPixels(int tw, int th, int[] palette, int palSize)
-    throws Exception {
-
-    int bppp = ((palSize > 16) ? 8 :
-                ((palSize > 4) ? 4 : ((palSize > 2) ? 2 : 1)));
-    int ptr = 0;
-
-    for (int i = 0; i < th; i++) {
-      int eol = ptr + tw;
-      int b = 0;
-      int nbits = 0;
-
-      while (ptr < eol) {
-        if (nbits == 0) {
-          b = zrleInStream.readU8();
-          nbits = 8;
-        }
-        nbits -= bppp;
-        int index = (b >> nbits) & ((1 << bppp) - 1) & 127;
-        if (bytesPerPixel == 1) {
-          zrleTilePixels8[ptr++] = (byte)palette[index];
-        } else {
-          zrleTilePixels24[ptr++] = palette[index];
-        }
-      }
-    }
-  }
-
-  private void readZrlePlainRLEPixels(int tw, int th) throws Exception {
-    int ptr = 0;
-    int end = ptr + tw * th;
-    while (ptr < end) {
-      int pix = readPixel(zrleInStream);
-      int len = 1;
-      int b;
-      do {
-        b = zrleInStream.readU8();
-        len += b;
-      } while (b == 255);
-
-      if (!(len <= end - ptr))
-        throw new Exception("ZRLE decoder: assertion failed" +
-                            " (len <= end-ptr)");
-
-      if (bytesPerPixel == 1) {
-        while (len-- > 0) zrleTilePixels8[ptr++] = (byte)pix;
-      } else {
-        while (len-- > 0) zrleTilePixels24[ptr++] = pix;
-      }
-    }
-  }
-
-  private void readZrlePackedRLEPixels(int tw, int th, int[] palette)
-    throws Exception {
-
-    int ptr = 0;
-    int end = ptr + tw * th;
-    while (ptr < end) {
-      int index = zrleInStream.readU8();
-      int len = 1;
-      if ((index & 128) != 0) {
-        int b;
-        do {
-          b = zrleInStream.readU8();
-          len += b;
-        } while (b == 255);
-
-        if (!(len <= end - ptr))
-          throw new Exception("ZRLE decoder: assertion failed" +
-                              " (len <= end - ptr)");
-      }
-
-      index &= 127;
-      int pix = palette[index];
-
-      if (bytesPerPixel == 1) {
-        while (len-- > 0) zrleTilePixels8[ptr++] = (byte)pix;
-      } else {
-        while (len-- > 0) zrleTilePixels24[ptr++] = pix;
-      }
-    }
-  }
-
-  //
-  // ZRLE encoder's data.
-  //
-
-  private byte[] zrleBuf;
-  private int zrleBufLen = 0;
-  private byte[] zrleTilePixels8;
-  private int[] zrleTilePixels24;
-  private ZlibInStream zrleInStream;
-  private boolean zrleRecWarningShown = false;
-}
diff --git a/java/src/com/tigervnc/decoder/ZlibDecoder.java b/java/src/com/tigervnc/decoder/ZlibDecoder.java
deleted file mode 100644
index 0eeaef5..0000000
--- a/java/src/com/tigervnc/decoder/ZlibDecoder.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package com.tigervnc.decoder;
-
-import com.tigervnc.vncviewer.RfbInputStream;
-import java.awt.Graphics;
-import java.io.IOException;
-import java.util.zip.DataFormatException;
-import java.util.zip.Inflater;
-
-//
-// Class that used for decoding ZLib encoded data.
-//
-
-public class ZlibDecoder extends RawDecoder {
-
-  final static int EncodingZlib = 6;
-
-  public ZlibDecoder(Graphics g, RfbInputStream is) {
-    super(g, is);
-  }
-
-  public ZlibDecoder(Graphics g, RfbInputStream is, int frameBufferW,
-                     int frameBufferH) {
-    super(g, is, frameBufferW, frameBufferH);
-  }
-
-  //
-  // Override handleRect method to decode ZLib encoded data insted of
-  // raw pixel data.
-  //
-
-  public void handleRect(int x, int y, int w, int h) throws IOException  {
-
-    //
-    // Write encoding ID to record output stream.
-    // Remark: we forced changed encoding from zlib to raw
-    // cause at this moment we cannot save data in zlib encoding.
-    //
-
-    if (dos != null) {
-      dos.writeInt(RawDecoder.EncodingRaw);
-    }
-
-    int nBytes = rfbis.readU32();
-
-    if (zlibBuf == null || zlibBufLen < nBytes) {
-      zlibBufLen = nBytes * 2;
-      zlibBuf = new byte[zlibBufLen];
-    }
-
-    rfbis.readFully(zlibBuf, 0, nBytes);
-
-    if (zlibInflater == null) {
-      zlibInflater = new Inflater();
-    }
-    zlibInflater.setInput(zlibBuf, 0, nBytes);
-
-    try {
-      if (bytesPerPixel == 1) {
-        for (int dy = y; dy < y + h; dy++) {
-          zlibInflater.inflate(pixels8, dy * framebufferWidth + x, w);
-
-          //
-          // Save decoded raw data to data output stream
-          //
-
-          if (dos != null)
-            dos.write(pixels8, dy * framebufferWidth + x, w);
-        }
-      } else {
-        byte[] buf = new byte[w * 4];
-        int i, offset;
-        for (int dy = y; dy < y + h; dy++) {
-          zlibInflater.inflate(buf);
-          offset = dy * framebufferWidth + x;
-          for (i = 0; i < w; i++) {
-            RawDecoder.pixels24[offset + i] =
-              (buf[i * 4 + 2] & 0xFF) << 16 |
-              (buf[i * 4 + 1] & 0xFF) << 8 |
-              (buf[i * 4] & 0xFF);
-          }
-
-          //
-          // Save decoded raw data to data output stream
-          //
-
-          if (dos != null)
-            dos.write(buf);
-        }
-      }
-    } catch (DataFormatException ex) {
-      ex.printStackTrace();
-    }
-    handleUpdatedPixels(x, y, w, h);
-  }
-
-  //
-  // Zlib encoder's data.
-  //
-
-  protected byte[] zlibBuf;
-  protected int zlibBufLen = 0;
-  protected Inflater zlibInflater;
-}
diff --git a/java/src/com/tigervnc/decoder/common/Repaintable.java b/java/src/com/tigervnc/decoder/common/Repaintable.java
deleted file mode 100644
index 896f09f..0000000
--- a/java/src/com/tigervnc/decoder/common/Repaintable.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.tigervnc.decoder.common;
-
-public interface Repaintable {
-
-  public void scheduleRepaint(int x, int y, int w, int h);
-
-}
diff --git a/java/src/com/tigervnc/vncviewer/MemInStream.java b/java/src/com/tigervnc/rdr/EndOfStream.java
similarity index 68%
copy from java/src/com/tigervnc/vncviewer/MemInStream.java
copy to java/src/com/tigervnc/rdr/EndOfStream.java
index 41a4fc0..bdcf7c2 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/rdr/EndOfStream.java
@@ -16,19 +16,10 @@
  * USA.
  */
 
-package com.tigervnc.vncviewer;
+package com.tigervnc.rdr;
 
-public class MemInStream extends InStream {
-
-  public MemInStream(byte[] data, int offset, int len) {
-    b = data;
-    ptr = offset;
-    end = offset + len;
-  }
-
-  public int pos() { return ptr; }
-
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    throw new Exception("MemInStream overrun: end of stream");
+public class EndOfStream extends Exception {
+  public EndOfStream() {
+    super("EndOfStream");
   }
 }
diff --git a/java/src/com/tigervnc/vncviewer/MemInStream.java b/java/src/com/tigervnc/rdr/Exception.java
similarity index 68%
copy from java/src/com/tigervnc/vncviewer/MemInStream.java
copy to java/src/com/tigervnc/rdr/Exception.java
index 41a4fc0..a5fe938 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/rdr/Exception.java
@@ -16,19 +16,10 @@
  * USA.
  */
 
-package com.tigervnc.vncviewer;
+package com.tigervnc.rdr;
 
-public class MemInStream extends InStream {
-
-  public MemInStream(byte[] data, int offset, int len) {
-    b = data;
-    ptr = offset;
-    end = offset + len;
-  }
-
-  public int pos() { return ptr; }
-
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    throw new Exception("MemInStream overrun: end of stream");
+public class Exception extends RuntimeException {
+  public Exception(String s) {
+    super(s);
   }
 }
diff --git a/java/src/com/tigervnc/vncviewer/MemInStream.java b/java/src/com/tigervnc/rdr/IOException.java
similarity index 68%
copy from java/src/com/tigervnc/vncviewer/MemInStream.java
copy to java/src/com/tigervnc/rdr/IOException.java
index 41a4fc0..6343d7a 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/rdr/IOException.java
@@ -16,19 +16,12 @@
  * USA.
  */
 
-package com.tigervnc.vncviewer;
+package com.tigervnc.rdr;
 
-public class MemInStream extends InStream {
-
-  public MemInStream(byte[] data, int offset, int len) {
-    b = data;
-    ptr = offset;
-    end = offset + len;
+class IOException extends Exception {
+  public IOException(java.io.IOException ex_) {
+    super(ex_.toString());
+    ex = ex_;
   }
-
-  public int pos() { return ptr; }
-
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    throw new Exception("MemInStream overrun: end of stream");
-  }
+  java.io.IOException ex;
 }
diff --git a/java/src/com/tigervnc/rdr/InStream.java b/java/src/com/tigervnc/rdr/InStream.java
new file mode 100644
index 0000000..ec2d6d7
--- /dev/null
+++ b/java/src/com/tigervnc/rdr/InStream.java
@@ -0,0 +1,196 @@
+/* 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.
+ */
+
+//
+// rdr::InStream marshalls data from a buffer stored in RDR (RFB Data
+// Representation).
+//
+
+package com.tigervnc.rdr;
+
+abstract public class InStream {
+
+  // check() ensures there is buffer data for at least one item of size
+  // itemSize bytes.  Returns the number of items in the buffer (up to a
+  // maximum of nItems).
+
+  public int check(int itemSize, int nItems) {
+    if (ptr + itemSize * nItems > end) {
+      if (ptr + itemSize > end)
+        return overrun(itemSize, nItems);
+
+      nItems = (end - ptr) / itemSize;
+    }
+    return nItems;
+  }
+
+  public final int check(int itemSize) {
+    if (ptr + itemSize > end)
+      return overrun(itemSize, 1);
+    return 1;
+  }
+
+  // checkNoWait() tries to make sure that the given number of bytes can
+  // be read without blocking.  It returns true if this is the case, false
+  // otherwise.  The length must be "small" (less than the buffer size).
+
+  public final boolean checkNoWait(int length) { return check(length, 1)!=0; }
+
+  // readU/SN() methods read unsigned and signed N-bit integers.
+
+  public final int readS8()  { check(1); return b[ptr++]; }
+  public final int readS16() { check(2); int b0 = b[ptr++];
+                               int b1 = b[ptr++] & 0xff; return b0 << 8 | b1; }
+  public final int readS32() { check(4); int b0 = b[ptr++];
+                               int b1 = b[ptr++] & 0xff;
+                               int b2 = b[ptr++] & 0xff;
+                               int b3 = b[ptr++] & 0xff;
+                               return b0 << 24 | b1 << 16 | b2 << 8 | b3; }
+
+  public final int readU8()  { return readS8()  & 0xff;  }
+  public final int readU16() { return readS16() & 0xffff; }
+  public final int readU32() { return readS32() & 0xffffffff; }
+
+  // readString() reads a string - a U32 length followed by the data.
+
+  public final String readString() {
+    int len = readU32();
+    if (len > maxStringLength)
+      throw new Exception("InStream max string length exceeded");
+
+    char[] str = new char[len];
+    int i = 0;
+    while (i < len) {
+      int j = i + check(1, len - i);
+      while (i < j) {
+	str[i++] = (char)b[ptr++];
+      }
+    }
+
+    return new String(str);
+  }
+
+  // maxStringLength protects against allocating a huge buffer.  Set it
+  // higher if you need longer strings.
+
+  public static int maxStringLength = 65535;
+
+  public final void skip(int bytes) {
+    while (bytes > 0) {
+      int n = check(1, bytes);
+      ptr += n;
+      bytes -= n;
+    }
+  }
+
+  // readBytes() reads an exact number of bytes into an array at an offset.
+
+  public void readBytes(byte[] data, int offset, int length) {
+    int offsetEnd = offset + length;
+    while (offset < offsetEnd) {
+      int n = check(1, offsetEnd - offset);
+      System.arraycopy(b, ptr, data, offset, n);
+      ptr += n;
+      offset += n;
+    }
+  }
+
+  public void readBytes(int[] data, int offset, int length) {
+    int offsetEnd = offset + length;
+    while (offset < offsetEnd) {
+      int n = check(1, offsetEnd - offset);
+      System.arraycopy(b, ptr, data, offset, n);
+      ptr += n;
+      offset += n;
+    }
+  }
+
+  // readOpaqueN() reads a quantity "without byte-swapping".  Because java has
+  // no byte-ordering, we just use big-endian.
+
+  public final int readOpaque8()  { return readU8(); }
+  public final int readOpaque16() { return readU16(); }
+  public final int readOpaque32() { return readU32(); }
+  public final int readOpaque24A() { check(3); int b0 = b[ptr++];
+                                     int b1 = b[ptr++]; int b2 = b[ptr++];
+                                     return b0 << 24 | b1 << 16 | b2 << 8; }
+  public final int readOpaque24B() { check(3); int b0 = b[ptr++];
+                                     int b1 = b[ptr++]; int b2 = b[ptr++];
+                                     return b0 << 16 | b1 << 8 | b2; }
+
+  public final int readPixel(int bytesPerPixel, boolean e) {
+    int[] pix = new int[4];
+    for (int i=0; i < bytesPerPixel; i++)
+      pix[i] = readU8();
+    if (e) {
+      return pix[0] << 16 | pix[1] << 8 | pix[2] | (0xff << 24);
+    } else {
+      return pix[2] << 16 | pix[1] << 8 | pix[0] | (0xff << 24);
+    }
+  }
+
+  public final void readPixels(int[] buf, int length, int bytesPerPixel, boolean e) {
+    for (int i = 0; i < length; i++)
+      buf[i] = readPixel(bytesPerPixel, e);
+  }
+
+  public final int readCompactLength() {
+    int b = readU8();
+    int result = b & 0x7F;
+    if ((b & 0x80) != 0) {
+      b = readU8();
+      result |= (b & 0x7F) << 7;
+      if ((b & 0x80) != 0) {
+        b = readU8();
+        result |= (b & 0xFF) << 14;
+      }
+    }
+    return result;
+  }
+  
+  // pos() returns the position in the stream.
+
+  abstract public int pos();
+
+  // bytesAvailable() returns true if at least one byte can be read from the
+  // stream without blocking.  i.e. if false is returned then readU8() would
+  // block.
+
+  public boolean bytesAvailable() { return end != ptr; }
+
+  // getbuf(), getptr(), getend() and setptr() are "dirty" methods which allow
+  // you to manipulate the buffer directly.  This is useful for a stream which
+  // is a wrapper around an underlying stream.
+
+  public final byte[] getbuf() { return b; }
+  public final int getptr() { return ptr; }
+  public final int getend() { return end; }
+  public final void setptr(int p) { ptr = p; }
+
+  // overrun() is implemented by a derived class to cope with buffer overrun.
+  // It ensures there are at least itemSize bytes of buffer data.  Returns
+  // the number of items in the buffer (up to a maximum of nItems).  itemSize
+  // is supposed to be "small" (a few bytes).
+
+  abstract protected int overrun(int itemSize, int nItems);
+
+  protected InStream() {}
+  protected byte[] b;
+  protected int ptr;
+  protected int end;
+}
diff --git a/java/src/com/tigervnc/rdr/JavaInStream.java b/java/src/com/tigervnc/rdr/JavaInStream.java
new file mode 100644
index 0000000..426a0e7
--- /dev/null
+++ b/java/src/com/tigervnc/rdr/JavaInStream.java
@@ -0,0 +1,147 @@
+/* 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.
+ */
+
+//
+// A JavaInStream reads from a java.io.InputStream
+//
+
+package com.tigervnc.rdr;
+
+public class JavaInStream extends InStream {
+
+  static final int defaultBufSize = 8192;
+  static final int minBulkSize = 1024;
+
+  public JavaInStream(java.io.InputStream jis_, int bufSize_) {
+    jis = jis_;
+    bufSize = bufSize_;
+    b = new byte[bufSize];
+    ptr = end = ptrOffset = 0;
+    timeWaitedIn100us = 5;
+    timedKbits = 0;
+  }
+
+  public JavaInStream(java.io.InputStream jis_) { this(jis_, defaultBufSize); }
+
+  public void readBytes(byte[] data, int offset, int length) {
+    if (length < minBulkSize) {
+      super.readBytes(data, offset, length);
+      return;
+    }
+
+    int n = end - ptr;
+    if (n > length) n = length;
+
+    System.arraycopy(b, ptr, data, offset, n);
+    offset += n;
+    length -= n;
+    ptr += n;
+
+    while (length > 0) {
+      n = read(data, offset, length);
+      offset += n;
+      length -= n;
+      ptrOffset += n;
+    }
+  }
+
+  public int pos() { return ptrOffset + ptr; }
+
+  public void startTiming() {
+    timing = true;
+
+    // Carry over up to 1s worth of previous rate for smoothing.
+
+    if (timeWaitedIn100us > 10000) {
+      timedKbits = timedKbits * 10000 / timeWaitedIn100us;
+      timeWaitedIn100us = 10000;
+    }
+  }
+
+  public void stopTiming() {
+    timing = false; 
+    if (timeWaitedIn100us < timedKbits/2)
+      timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s
+  }
+
+  public long kbitsPerSecond() {
+    return timedKbits * 10000 / timeWaitedIn100us;
+  }
+
+  public long timeWaited() { return timeWaitedIn100us; }
+
+  protected int overrun(int itemSize, int nItems) {
+    if (itemSize > bufSize)
+      throw new Exception("JavaInStream overrun: max itemSize exceeded");
+
+    if (end - ptr != 0)
+      System.arraycopy(b, ptr, b, 0, end - ptr);
+
+    ptrOffset += ptr;
+    end -= ptr;
+    ptr = 0;
+
+    while (end < itemSize) {
+      int n = read(b, end, bufSize - end);
+      end += n;
+    }
+
+    if (itemSize * nItems > end)
+      nItems = end / itemSize;
+
+    return nItems;
+  }
+
+  private int read(byte[] buf, int offset, int len) {
+    try {
+      long before = 0;
+      if (timing)
+        before = System.currentTimeMillis();
+
+      int n = jis.read(buf, offset, len);
+      if (n < 0) throw new EndOfStream();
+
+      if (timing) {
+        long after = System.currentTimeMillis();
+        long newTimeWaited = (after - before) * 10;
+        int newKbits = n * 8 / 1000;
+
+        // limit rate to between 10kbit/s and 40Mbit/s
+
+        if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000;
+        if (newTimeWaited < newKbits/4)    newTimeWaited = newKbits/4;
+
+        timeWaitedIn100us += newTimeWaited;
+        timedKbits += newKbits;
+      }
+
+      return n;
+
+    } catch (java.io.IOException e) {
+      throw new IOException(e);
+    }
+  }
+
+  private java.io.InputStream jis;
+  private int ptrOffset;
+  private int bufSize;
+
+  boolean timing;
+  long timeWaitedIn100us;
+  long timedKbits;
+}
diff --git a/java/src/com/tigervnc/rdr/JavaOutStream.java b/java/src/com/tigervnc/rdr/JavaOutStream.java
new file mode 100644
index 0000000..94791b9
--- /dev/null
+++ b/java/src/com/tigervnc/rdr/JavaOutStream.java
@@ -0,0 +1,82 @@
+/* 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.
+ */
+
+//
+// A JavaOutStream writes to a java.io.OutputStream
+//
+
+package com.tigervnc.rdr;
+
+public class JavaOutStream extends OutStream {
+
+  static final int defaultBufSize = 16384;
+  static final int minBulkSize = 1024;
+
+  public JavaOutStream(java.io.OutputStream jos_, int bufSize_) {
+    jos = jos_;
+    bufSize = bufSize_;
+    b = new byte[bufSize];
+    ptr = 0;
+    end = bufSize;
+  }
+
+  public JavaOutStream(java.io.OutputStream jos) { this(jos, defaultBufSize); }
+
+  public void writeBytes(byte[] data, int offset, int length) {
+    if (length < minBulkSize) {
+      super.writeBytes(data, offset, length);
+      return;
+    }
+
+    flush();
+    try {
+      jos.write(data, offset, length);
+    } catch (java.io.IOException e) {
+      throw new IOException(e);
+    }
+    ptrOffset += length;
+  }
+
+  public void flush() {
+    try {
+      jos.write(b, 0, ptr);
+    } catch (java.io.IOException e) {
+      throw new IOException(e);
+    }
+    ptrOffset += ptr;
+    ptr = 0;
+  }
+
+  public int length() { return ptrOffset + ptr; }
+
+  protected int overrun(int itemSize, int nItems) {
+    if (itemSize > bufSize)
+      throw new Exception("JavaOutStream overrun: max itemSize exceeded");
+
+    flush();
+
+    if (itemSize * nItems > end)
+      nItems = end / itemSize;
+
+    return nItems;
+  }
+
+  private java.io.OutputStream jos;
+  private int ptrOffset;
+  private int bufSize;
+}
diff --git a/java/src/com/tigervnc/vncviewer/MemInStream.java b/java/src/com/tigervnc/rdr/MemInStream.java
similarity index 85%
rename from java/src/com/tigervnc/vncviewer/MemInStream.java
rename to java/src/com/tigervnc/rdr/MemInStream.java
index 41a4fc0..ce4f91e 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/rdr/MemInStream.java
@@ -16,7 +16,7 @@
  * USA.
  */
 
-package com.tigervnc.vncviewer;
+package com.tigervnc.rdr;
 
 public class MemInStream extends InStream {
 
@@ -28,7 +28,7 @@
 
   public int pos() { return ptr; }
 
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    throw new Exception("MemInStream overrun: end of stream");
+  protected int overrun(int itemSize, int nItems) {
+    throw new EndOfStream();
   }
 }
diff --git a/java/src/com/tigervnc/rdr/MemOutStream.java b/java/src/com/tigervnc/rdr/MemOutStream.java
new file mode 100644
index 0000000..b304079
--- /dev/null
+++ b/java/src/com/tigervnc/rdr/MemOutStream.java
@@ -0,0 +1,53 @@
+/* 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.
+ */
+
+//
+// A MemOutStream grows as needed when data is written to it.
+//
+
+package com.tigervnc.rdr;
+
+public class MemOutStream extends OutStream {
+
+  public MemOutStream(int len) {
+    b = new byte[len];
+    ptr = 0;
+    end = len;
+  }
+  public MemOutStream() { this(1024); }
+
+  public int length() { return ptr; }
+  public void clear() { ptr = 0; };
+  public void reposition(int pos) { ptr = pos; }
+
+  // overrun() either doubles the buffer or adds enough space for nItems of
+  // size itemSize bytes.
+
+  protected int overrun(int itemSize, int nItems) {
+    int len = ptr + itemSize * nItems;
+    if (len < end * 2)
+      len = end * 2;
+
+    byte[] newBuf = new byte[len];
+    System.arraycopy(b, 0, newBuf, 0, ptr);
+    b = newBuf;
+    end = len;
+
+    return nItems;
+  }
+}
diff --git a/java/src/com/tigervnc/rdr/OutStream.java b/java/src/com/tigervnc/rdr/OutStream.java
new file mode 100644
index 0000000..7b4869e
--- /dev/null
+++ b/java/src/com/tigervnc/rdr/OutStream.java
@@ -0,0 +1,141 @@
+/* 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.
+ */
+
+//
+// rdr::OutStream marshalls data into a buffer stored in RDR (RFB Data
+// Representation).
+//
+
+package com.tigervnc.rdr;
+
+abstract public class OutStream {
+
+  // check() ensures there is buffer space for at least one item of size
+  // itemSize bytes.  Returns the number of items which fit (up to a maximum
+  // of nItems).
+
+  public final int check(int itemSize, int nItems) {
+    if (ptr + itemSize * nItems > end) {
+      if (ptr + itemSize > end)
+        return overrun(itemSize, nItems);
+
+      nItems = (end - ptr) / itemSize;
+    }
+    return nItems;
+  }
+
+  public final void check(int itemSize) {
+    if (ptr + itemSize > end)
+      overrun(itemSize, 1);
+  }
+
+  // writeU/SN() methods write unsigned and signed N-bit integers.
+
+  public final void writeU8( int u) { check(1); b[ptr++] = (byte)u; }
+  public final void writeU16(int u) { check(2); b[ptr++] = (byte)(u >> 8);
+                                      b[ptr++] = (byte)u; }
+  public final void writeU32(int u) { check(4); b[ptr++] = (byte)(u >> 24);
+                                      b[ptr++] = (byte)(u >> 16);
+                                      b[ptr++] = (byte)(u >> 8);
+                                      b[ptr++] = (byte)u; }
+
+  public final void writeS8( int s) { writeU8( s); }
+  public final void writeS16(int s) { writeU16(s); }
+  public final void writeS32(int s) { writeU32(s); }
+
+  // writeString() writes a string - a U32 length followed by the data.
+
+  public final void writeString(String str) {
+    int len = str.length();
+    writeU32(len);
+    for (int i = 0; i < len;) {
+      int j = i + check(1, len - i);
+      while (i < j) {
+	b[ptr++] = (byte)str.charAt(i++);
+      }
+    }
+  }
+
+  public final void pad(int bytes) {
+    while (bytes-- > 0) writeU8(0);
+  }
+
+  public final void skip(int bytes) {
+    while (bytes > 0) {
+      int n = check(1, bytes);
+      ptr += n;
+      bytes -= n;
+    }
+  }
+
+  // writeBytes() writes an exact number of bytes from an array at an offset.
+
+  public void writeBytes(byte[] data, int offset, int length) {
+    int offsetEnd = offset + length;
+    while (offset < offsetEnd) {
+      int n = check(1, offsetEnd - offset);
+      System.arraycopy(data, offset, b, ptr, n);
+      ptr += n;
+      offset += n;
+    }
+  }
+
+  // writeOpaqueN() writes a quantity without byte-swapping.  Because java has
+  // no byte-ordering, we just use big-endian.
+
+  public final void writeOpaque8( int u) { writeU8( u); }
+  public final void writeOpaque16(int u) { writeU16(u); }
+  public final void writeOpaque32(int u) { writeU32(u); }
+  public final void writeOpaque24A(int u) { check(3);
+                                            b[ptr++] = (byte)(u >> 24);
+                                            b[ptr++] = (byte)(u >> 16);
+                                            b[ptr++] = (byte)(u >> 8); }
+  public final void writeOpaque24B(int u) { check(3);
+                                            b[ptr++] = (byte)(u >> 16);
+                                            b[ptr++] = (byte)(u >> 8);
+                                            b[ptr++] = (byte)u; }
+
+  // length() returns the length of the stream.
+
+  abstract public int length();
+
+  // flush() requests that the stream be flushed.
+
+  public void flush() {}
+
+  // getptr(), getend() and setptr() are "dirty" methods which allow you to
+  // manipulate the buffer directly.  This is useful for a stream which is a
+  // wrapper around an underlying stream.
+
+  public final byte[] getbuf() { return b; }
+  public final int getptr() { return ptr; }
+  public final int getend() { return end; }
+  public final void setptr(int p) { ptr = p; }
+
+  // overrun() is implemented by a derived class to cope with buffer overrun.
+  // It ensures there are at least itemSize bytes of buffer space.  Returns
+  // the number of items which fit (up to a maximum of nItems).  itemSize is
+  // supposed to be "small" (a few bytes).
+
+  abstract protected int overrun(int itemSize, int nItems);
+
+  protected OutStream() {}
+  protected byte[] b;
+  protected int ptr;
+  protected int end;
+}
diff --git a/java/src/com/tigervnc/vncviewer/ZlibInStream.java b/java/src/com/tigervnc/rdr/ZlibInStream.java
similarity index 93%
rename from java/src/com/tigervnc/vncviewer/ZlibInStream.java
rename to java/src/com/tigervnc/rdr/ZlibInStream.java
index 2f2a3c0..64de00a 100644
--- a/java/src/com/tigervnc/vncviewer/ZlibInStream.java
+++ b/java/src/com/tigervnc/rdr/ZlibInStream.java
@@ -20,7 +20,7 @@
 // A ZlibInStream reads from a zlib.io.InputStream
 //
 
-package com.tigervnc.vncviewer;
+package com.tigervnc.rdr;
 
 public class ZlibInStream extends InStream {
 
@@ -41,7 +41,7 @@
     ptr = end = 0;
   }
 
-  public void reset() throws Exception {
+  public void reset() {
     ptr = end = 0;
     if (underlying == null) return;
 
@@ -54,7 +54,7 @@
 
   public int pos() { return ptrOffset + ptr; }
 
-  protected int overrun(int itemSize, int nItems) throws Exception {
+  protected int overrun(int itemSize, int nItems) {
     if (itemSize > bufSize)
       throw new Exception("ZlibInStream overrun: max itemSize exceeded");
     if (underlying == null)
@@ -82,7 +82,7 @@
   // data.  Returns false if wait is false and we would block on the underlying
   // stream.
 
-  private void decompress() throws Exception {
+  private void decompress() {
     try {
       underlying.check(1);
       int avail_in = underlying.getend() - underlying.getptr();
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/vncviewer/MemInStream.java b/java/src/com/tigervnc/rfb/AuthFailureException.java
similarity index 67%
copy from java/src/com/tigervnc/vncviewer/MemInStream.java
copy to java/src/com/tigervnc/rfb/AuthFailureException.java
index 41a4fc0..35fabef 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/rfb/AuthFailureException.java
@@ -16,19 +16,8 @@
  * USA.
  */
 
-package com.tigervnc.vncviewer;
+package com.tigervnc.rfb;
 
-public class MemInStream extends InStream {
-
-  public MemInStream(byte[] data, int offset, int len) {
-    b = data;
-    ptr = offset;
-    end = offset + len;
-  }
-
-  public int pos() { return ptr; }
-
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    throw new Exception("MemInStream overrun: end of stream");
-  }
+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/vncviewer/MemInStream.java b/java/src/com/tigervnc/rfb/CMsgWriterV3.java
similarity index 68%
copy from java/src/com/tigervnc/vncviewer/MemInStream.java
copy to java/src/com/tigervnc/rfb/CMsgWriterV3.java
index 41a4fc0..24d4756 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/rfb/CMsgWriterV3.java
@@ -16,19 +16,24 @@
  * USA.
  */
 
-package com.tigervnc.vncviewer;
+package com.tigervnc.rfb;
 
-public class MemInStream extends InStream {
+import com.tigervnc.rdr.*;
 
-  public MemInStream(byte[] data, int offset, int len) {
-    b = data;
-    ptr = offset;
-    end = offset + len;
+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 int pos() { return ptr; }
+  public void startMsg(int type) {
+    os.writeU8(type);
+  }
 
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    throw new Exception("MemInStream overrun: end of stream");
+  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/vncviewer/MemInStream.java b/java/src/com/tigervnc/rfb/CSecurityNone.java
similarity index 67%
copy from java/src/com/tigervnc/vncviewer/MemInStream.java
copy to java/src/com/tigervnc/rfb/CSecurityNone.java
index 41a4fc0..e31056d 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/rfb/CSecurityNone.java
@@ -16,19 +16,12 @@
  * USA.
  */
 
-package com.tigervnc.vncviewer;
+package com.tigervnc.rfb;
 
-public class MemInStream extends InStream {
+public class CSecurityNone extends CSecurity {
 
-  public MemInStream(byte[] data, int offset, int len) {
-    b = data;
-    ptr = offset;
-    end = offset + len;
-  }
-
-  public int pos() { return ptr; }
-
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    throw new Exception("MemInStream overrun: end of stream");
-  }
+  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/vncviewer/MemInStream.java b/java/src/com/tigervnc/rfb/ConnFailedException.java
similarity index 67%
copy from java/src/com/tigervnc/vncviewer/MemInStream.java
copy to java/src/com/tigervnc/rfb/ConnFailedException.java
index 41a4fc0..d1ddcb4 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/rfb/ConnFailedException.java
@@ -16,19 +16,8 @@
  * USA.
  */
 
-package com.tigervnc.vncviewer;
+package com.tigervnc.rfb;
 
-public class MemInStream extends InStream {
-
-  public MemInStream(byte[] data, int offset, int len) {
-    b = data;
-    ptr = offset;
-    end = offset + len;
-  }
-
-  public int pos() { return ptr; }
-
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    throw new Exception("MemInStream overrun: end of stream");
-  }
+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/vncviewer/MemInStream.java b/java/src/com/tigervnc/rfb/Cursor.java
similarity index 68%
copy from java/src/com/tigervnc/vncviewer/MemInStream.java
copy to java/src/com/tigervnc/rfb/Cursor.java
index 41a4fc0..420eb82 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/rfb/Cursor.java
@@ -16,19 +16,19 @@
  * USA.
  */
 
-package com.tigervnc.vncviewer;
+package com.tigervnc.rfb;
 
-public class MemInStream extends InStream {
+import java.awt.*;
 
-  public MemInStream(byte[] data, int offset, int len) {
-    b = data;
-    ptr = offset;
-    end = offset + len;
+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 int pos() { return ptr; }
-
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    throw new Exception("MemInStream overrun: end of stream");
-  }
+  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/vncviewer/DesCipher.java b/java/src/com/tigervnc/rfb/DesCipher.java
similarity index 99%
rename from java/src/com/tigervnc/vncviewer/DesCipher.java
rename to java/src/com/tigervnc/rfb/DesCipher.java
index 25362fe..f7ae9db 100644
--- a/java/src/com/tigervnc/vncviewer/DesCipher.java
+++ b/java/src/com/tigervnc/rfb/DesCipher.java
@@ -70,10 +70,6 @@
 // fine Java utilities: http://www.acme.com/java/
 
 
-package com.tigervnc.vncviewer;
-
-import java.io.*;
-
 /// The DES encryption method.
 // <P>
 // This is surprisingly fast, for pure Java.  On a SPARC 20, wrapped
@@ -90,6 +86,8 @@
 // @see EncryptedOutputStream
 // @see EncryptedInputStream
 
+package com.tigervnc.rfb;
+
 public class DesCipher
     {
 
diff --git a/java/src/com/tigervnc/vncviewer/MemInStream.java b/java/src/com/tigervnc/rfb/Encoder.java
similarity index 68%
copy from java/src/com/tigervnc/vncviewer/MemInStream.java
copy to java/src/com/tigervnc/rfb/Encoder.java
index 41a4fc0..0964f88 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/rfb/Encoder.java
@@ -16,19 +16,10 @@
  * USA.
  */
 
-package com.tigervnc.vncviewer;
+package com.tigervnc.rfb;
 
-public class MemInStream extends InStream {
-
-  public MemInStream(byte[] data, int offset, int len) {
-    b = data;
-    ptr = offset;
-    end = offset + len;
-  }
-
-  public int pos() { return ptr; }
-
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    throw new Exception("MemInStream overrun: end of stream");
+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/vncviewer/MemInStream.java b/java/src/com/tigervnc/rfb/Exception.java
similarity index 67%
copy from java/src/com/tigervnc/vncviewer/MemInStream.java
copy to java/src/com/tigervnc/rfb/Exception.java
index 41a4fc0..26ac355 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/rfb/Exception.java
@@ -16,19 +16,8 @@
  * USA.
  */
 
-package com.tigervnc.vncviewer;
+package com.tigervnc.rfb;
 
-public class MemInStream extends InStream {
-
-  public MemInStream(byte[] data, int offset, int len) {
-    b = data;
-    ptr = offset;
-    end = offset + len;
-  }
-
-  public int pos() { return ptr; }
-
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    throw new Exception("MemInStream overrun: end of stream");
-  }
+public class Exception extends com.tigervnc.rdr.Exception {
+  public Exception(String s) { super(s); }
 }
diff --git a/java/src/com/tigervnc/vncviewer/MemInStream.java b/java/src/com/tigervnc/rfb/Hextile.java
similarity index 67%
copy from java/src/com/tigervnc/vncviewer/MemInStream.java
copy to java/src/com/tigervnc/rfb/Hextile.java
index 41a4fc0..9c05b72 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/rfb/Hextile.java
@@ -16,19 +16,12 @@
  * USA.
  */
 
-package com.tigervnc.vncviewer;
+package com.tigervnc.rfb;
 
-public class MemInStream extends InStream {
-
-  public MemInStream(byte[] data, int offset, int len) {
-    b = data;
-    ptr = offset;
-    end = offset + len;
-  }
-
-  public int pos() { return ptr; }
-
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    throw new Exception("MemInStream overrun: end of stream");
-  }
+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/vncviewer/MemInStream.java b/java/src/com/tigervnc/rfb/ManagedPixelBuffer.java
similarity index 65%
copy from java/src/com/tigervnc/vncviewer/MemInStream.java
copy to java/src/com/tigervnc/rfb/ManagedPixelBuffer.java
index 41a4fc0..46b5acf 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/rfb/ManagedPixelBuffer.java
@@ -16,19 +16,23 @@
  * USA.
  */
 
-package com.tigervnc.vncviewer;
+package com.tigervnc.rfb;
 
-public class MemInStream extends InStream {
-
-  public MemInStream(byte[] data, int offset, int len) {
-    b = data;
-    ptr = offset;
-    end = offset + len;
+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 pos() { return ptr; }
+  public int dataLen() { return area(); }
 
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    throw new Exception("MemInStream overrun: end of stream");
+  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/vncviewer/MemInStream.java b/java/src/com/tigervnc/rfb/UserMsgBox.java
similarity index 67%
copy from java/src/com/tigervnc/vncviewer/MemInStream.java
copy to java/src/com/tigervnc/rfb/UserMsgBox.java
index 41a4fc0..e83d995 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/rfb/UserMsgBox.java
@@ -16,19 +16,12 @@
  * USA.
  */
 
-package com.tigervnc.vncviewer;
+// 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.
 
-public class MemInStream extends InStream {
+package com.tigervnc.rfb;
 
-  public MemInStream(byte[] data, int offset, int len) {
-    b = data;
-    ptr = offset;
-    end = offset + len;
-  }
-
-  public int pos() { return ptr; }
-
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    throw new Exception("MemInStream overrun: end of stream");
-  }
+public interface UserMsgBox {
+  public boolean showMsgBox(int flags,String title, String text);
 }
diff --git a/java/src/com/tigervnc/vncviewer/MemInStream.java b/java/src/com/tigervnc/rfb/UserPasswdGetter.java
similarity index 67%
copy from java/src/com/tigervnc/vncviewer/MemInStream.java
copy to java/src/com/tigervnc/rfb/UserPasswdGetter.java
index 41a4fc0..9796b66 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/rfb/UserPasswdGetter.java
@@ -16,19 +16,12 @@
  * USA.
  */
 
-package com.tigervnc.vncviewer;
+// 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.
 
-public class MemInStream extends InStream {
+package com.tigervnc.rfb;
 
-  public MemInStream(byte[] data, int offset, int len) {
-    b = data;
-    ptr = offset;
-    end = offset + len;
-  }
-
-  public int pos() { return ptr; }
-
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    throw new Exception("MemInStream overrun: end of stream");
-  }
+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
+
+}
diff --git a/java/src/com/tigervnc/vncviewer/AuthPanel.java b/java/src/com/tigervnc/vncviewer/AuthPanel.java
deleted file mode 100644
index a23758e..0000000
--- a/java/src/com/tigervnc/vncviewer/AuthPanel.java
+++ /dev/null
@@ -1,125 +0,0 @@
-//
-//  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
-//  Copyright (C) 2002-2006 Constantin Kaplinsky.  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.vncviewer;
-
-import java.awt.*;
-import java.awt.event.*;
-
-//
-// The panel which implements the user authentication scheme
-//
-
-class AuthPanel extends Panel implements ActionListener {
-
-  TextField passwordField;
-  Button okButton;
-  boolean AskPassword;
-
-  //
-  // Constructor.
-  //
-
-  public AuthPanel(VncViewer viewer, boolean askpassword)
-  {
-    AskPassword = askpassword;
-    Label titleLabel = new Label("VNC Authentication", Label.CENTER);
-    titleLabel.setFont(new Font("Helvetica", Font.BOLD, 18));
-
-    Label promptLabel;
-    if (AskPassword)
-      promptLabel = new Label("Password:", Label.CENTER);
-    else
-      promptLabel = new Label("User:", Label.CENTER);
-
-    passwordField = new TextField(10);
-    passwordField.setForeground(Color.black);
-    passwordField.setBackground(Color.white);
-    if (AskPassword)
-      passwordField.setEchoChar('*');
-
-    okButton = new Button("OK");
-
-    GridBagLayout gridbag = new GridBagLayout();
-    GridBagConstraints gbc = new GridBagConstraints();
-
-    setLayout(gridbag);
-
-    gbc.gridwidth = GridBagConstraints.REMAINDER;
-    gbc.insets = new Insets(0,0,20,0);
-    gridbag.setConstraints(titleLabel,gbc);
-    add(titleLabel);
-
-    gbc.fill = GridBagConstraints.NONE;
-    gbc.gridwidth = 1;
-    gbc.insets = new Insets(0,0,0,0);
-    gridbag.setConstraints(promptLabel,gbc);
-    add(promptLabel);
-
-    gridbag.setConstraints(passwordField,gbc);
-    add(passwordField);
-    passwordField.addActionListener(this);
-
-    // gbc.ipady = 10;
-    gbc.gridwidth = GridBagConstraints.REMAINDER;
-    gbc.fill = GridBagConstraints.BOTH;
-    gbc.insets = new Insets(0,20,0,0);
-    gbc.ipadx = 30;
-    gridbag.setConstraints(okButton,gbc);
-    add(okButton);
-    okButton.addActionListener(this);
-  }
-
-  //
-  // Move keyboard focus to the default object, that is, the password
-  // text field.
-  //
-
-  public void moveFocusToDefaultField()
-  {
-    passwordField.requestFocus();
-  }
-
-  //
-  // This method is called when a button is pressed or return is
-  // pressed in the password text field.
-  //
-
-  public synchronized void actionPerformed(ActionEvent evt)
-  {
-    if (evt.getSource() == passwordField || evt.getSource() == okButton) {
-      passwordField.setEnabled(false);
-      notify();
-    }
-  }
-
-  //
-  // Wait for user entering a password, and return it as String.
-  //
-
-  public synchronized String getPassword() throws Exception
-  {
-    try {
-      wait();
-    } catch (InterruptedException e) { }
-    return passwordField.getText();
-  }
-
-}
diff --git a/java/src/com/tigervnc/vncviewer/ButtonPanel.java b/java/src/com/tigervnc/vncviewer/ButtonPanel.java
deleted file mode 100644
index 1ef6412..0000000
--- a/java/src/com/tigervnc/vncviewer/ButtonPanel.java
+++ /dev/null
@@ -1,154 +0,0 @@
-//
-//  Copyright (C) 2001,2002 HorizonLive.com, Inc.  All Rights Reserved.
-//  Copyright (C) 1999 AT&T Laboratories Cambridge.  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.
-//
-
-//
-// ButtonPanel class implements panel with four buttons in the
-// VNCViewer desktop window.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.io.*;
-
-class ButtonPanel extends Panel implements ActionListener {
-
-  VncViewer viewer;
-  Button disconnectButton;
-  Button optionsButton;
-  Button recordButton;
-  Button clipboardButton;
-  Button ctrlAltDelButton;
-  Button refreshButton;
-
-  ButtonPanel(VncViewer v) {
-    viewer = v;
-
-    setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
-    disconnectButton = new Button("Disconnect");
-    disconnectButton.setEnabled(false);
-    add(disconnectButton);
-    disconnectButton.addActionListener(this);
-    optionsButton = new Button("Options");
-    add(optionsButton);
-    optionsButton.addActionListener(this);
-    clipboardButton = new Button("Clipboard");
-    clipboardButton.setEnabled(false);
-    add(clipboardButton);
-    clipboardButton.addActionListener(this);
-    if (viewer.rec != null) {
-      recordButton = new Button("Record");
-      add(recordButton);
-      recordButton.addActionListener(this);
-    }
-    ctrlAltDelButton = new Button("Send Ctrl-Alt-Del");
-    ctrlAltDelButton.setEnabled(false);
-    add(ctrlAltDelButton);
-    ctrlAltDelButton.addActionListener(this);
-    refreshButton = new Button("Refresh");
-    refreshButton.setEnabled(false);
-    add(refreshButton);
-    refreshButton.addActionListener(this);
-  }
-
-  //
-  // Enable buttons on successful connection.
-  //
-
-  public void enableButtons() {
-    disconnectButton.setEnabled(true);
-    clipboardButton.setEnabled(true);
-    refreshButton.setEnabled(true);
-  }
-
-  //
-  // Disable all buttons on disconnect.
-  //
-
-  public void disableButtonsOnDisconnect() {
-    remove(disconnectButton);
-    disconnectButton = new Button("Hide desktop");
-    disconnectButton.setEnabled(true);
-    add(disconnectButton, 0);
-    disconnectButton.addActionListener(this);
-
-    optionsButton.setEnabled(false);
-    clipboardButton.setEnabled(false);
-    ctrlAltDelButton.setEnabled(false);
-    refreshButton.setEnabled(false);
-  }
-
-  //
-  // Enable/disable controls that should not be available in view-only
-  // mode.
-  //
-
-  public void enableRemoteAccessControls(boolean enable) {
-    ctrlAltDelButton.setEnabled(enable);
-  }
-
-  //
-  // Event processing.
-  //
-
-  public void actionPerformed(ActionEvent evt) {
-
-    viewer.moveFocusToDesktop();
-
-    if (evt.getSource() == disconnectButton) {
-      viewer.disconnect();
-
-    } else if (evt.getSource() == optionsButton) {
-      viewer.options.setVisible(!viewer.options.isVisible());
-
-    } else if (evt.getSource() == recordButton) {
-      viewer.rec.setVisible(!viewer.rec.isVisible());
-
-    } else if (evt.getSource() == clipboardButton) {
-      viewer.clipboard.setVisible(!viewer.clipboard.isVisible());
-
-    } else if (evt.getSource() == ctrlAltDelButton) {
-      try {
-        final int modifiers = InputEvent.CTRL_MASK | InputEvent.ALT_MASK;
-
-        KeyEvent ctrlAltDelEvent =
-          new KeyEvent(this, KeyEvent.KEY_PRESSED, 0, modifiers, 127);
-        viewer.rfb.writeKeyEvent(ctrlAltDelEvent);
-
-        ctrlAltDelEvent =
-          new KeyEvent(this, KeyEvent.KEY_RELEASED, 0, modifiers, 127);
-        viewer.rfb.writeKeyEvent(ctrlAltDelEvent);
-
-      } catch (IOException e) {
-        e.printStackTrace();
-      }
-    } else if (evt.getSource() == refreshButton) {
-      try {
-	RfbProto rfb = viewer.rfb;
-	rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth,
-					  rfb.framebufferHeight, false);
-      } catch (IOException e) {
-        e.printStackTrace();
-      }
-    }
-  }
-}
-
diff --git a/java/src/com/tigervnc/vncviewer/CConn.java b/java/src/com/tigervnc/vncviewer/CConn.java
new file mode 100644
index 0000000..e4da169
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/CConn.java
@@ -0,0 +1,1137 @@
+/* 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.

+ */

+
+//

+// CConn

+//

+// Methods on CConn are called from both the GUI thread and the thread which

+// processes incoming RFB messages ("the RFB thread").  This means we need to

+// be careful with synchronization here.

+//

+// Any access to writer() must not only be synchronized, but we must also make

+// sure that the connection is in RFBSTATE_NORMAL.  We are guaranteed this for

+// any code called after serverInit() has been called.  Since the DesktopWindow

+// isn't created until then, any methods called only from DesktopWindow can

+// assume that we are in RFBSTATE_NORMAL.

+

+package com.tigervnc.vncviewer;

+

+import java.awt.*;

+import java.awt.event.*;

+import java.awt.Component;

+import java.awt.Dimension;

+import java.awt.Event;

+import java.awt.Frame;

+import java.awt.ScrollPane;

+

+import java.io.*;

+import javax.net.ssl.*;

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+import java.io.File;

+import java.util.jar.Attributes;

+import java.util.jar.Manifest;

+import javax.swing.*;

+import javax.swing.filechooser.*;

+import javax.swing.ImageIcon;

+import java.net.URL;

+import java.net.ServerSocket;

+import javax.swing.border.*;

+import java.util.*;

+
+import com.tigervnc.rdr.*;
+import com.tigervnc.rfb.*;
+import com.tigervnc.rfb.Exception;
+import com.tigervnc.rfb.Point;
+import com.tigervnc.rfb.Rect;
+

+class ViewportFrame extends JFrame 

+{

+  public ViewportFrame(String name, CConn cc_) {

+    cc = cc_;

+    setTitle("TigerVNC: "+name);

+    setFocusable(false);

+    setFocusTraversalKeysEnabled(false);

+    addWindowFocusListener(new WindowAdapter() {

+      public void windowGainedFocus(WindowEvent e) {

+        sp.getViewport().getView().requestFocusInWindow();

+      }

+    });

+    addWindowListener(new WindowAdapter() {

+      public void windowClosing(WindowEvent e) {

+        cc.close();

+      }

+    });

+  }

+

+  public void addChild(DesktopWindow child) {

+    sp = new JScrollPane(child);

+    child.setBackground(Color.BLACK);

+    child.setOpaque(true);

+    sp.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));

+    getContentPane().add(sp);

+  }

+

+  public void setGeometry(int x, int y, int w, int h) {

+    pack();

+    if (cc.fullScreen) setSize(w, h);

+    setLocation(x, y);

+    setBackground(Color.BLACK);

+  }

+

+

+  CConn cc;

+  Graphics g;

+  JScrollPane sp;

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

+}

+

+public class CConn extends CConnection

+  implements UserPasswdGetter, UserMsgBox, OptionsDialogCallback

+{

+  ////////////////////////////////////////////////////////////////////

+  // The following methods are all called from the RFB thread

+

+  public CConn(VncViewer viewer_, java.net.Socket sock_, 

+               String vncServerName, boolean reverse) 

+  {

+    serverHost = null; serverPort = 0; sock = sock_; viewer = viewer_; 

+    currentEncoding = Encodings.encodingTight; lastServerEncoding = -1;

+    fullColour = viewer.fullColour.getValue();

+    lowColourLevel = 2;

+    autoSelect = viewer.autoSelect.getValue();

+    shared = viewer.shared.getValue(); formatChange = false;

+    encodingChange = false; sameMachine = false;

+    fullScreen = viewer.fullScreen.getValue();

+    menuKey = Keysyms.F8;

+    options = new OptionsDialog(this);

+    clipboardDialog = new ClipboardDialog(this);

+    firstUpdate = true; pendingUpdate = false;

+

+    setShared(shared);

+    upg = this;

+    msg = this;

+

+    String encStr = viewer.preferredEncoding.getValue();

+    int encNum = Encodings.encodingNum(encStr);

+    if (encNum != -1) {

+      currentEncoding = encNum;

+    }

+    cp.supportsDesktopResize = true;

+    cp.supportsExtendedDesktopSize = true;

+    cp.supportsDesktopRename = true;

+    cp.supportsLocalCursor = viewer.useLocalCursor.getValue();

+    cp.customCompressLevel = viewer.customCompressLevel.getValue();

+    cp.compressLevel = viewer.compressLevel.getValue();

+    cp.noJpeg = viewer.noJpeg.getValue();

+    cp.qualityLevel = viewer.qualityLevel.getValue();

+    initMenu();

+

+    if (sock != null) {

+      String name = sock.getRemoteSocketAddress()+"::"+sock.getPort();

+      vlog.info("Accepted connection from "+name);

+    } else {

+      if (vncServerName != null) {

+        serverHost = Hostname.getHost(vncServerName);

+        serverPort = Hostname.getPort(vncServerName);

+      } else {

+        ServerDialog dlg = new ServerDialog(options, vncServerName, this);

+        if (!dlg.showDialog() || dlg.server.getSelectedItem().equals("")) {

+          System.exit(1);

+        }

+        vncServerName = (String)dlg.server.getSelectedItem();

+        serverHost = Hostname.getHost(vncServerName);

+        serverPort = Hostname.getPort(vncServerName);

+      }

+

+      try {

+        sock = new java.net.Socket(serverHost, serverPort);

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

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

+      }

+      vlog.info("connected to host "+serverHost+" port "+serverPort);

+    }

+

+    sameMachine = (sock.getLocalSocketAddress() == sock.getRemoteSocketAddress());

+    try {

+      sock.setTcpNoDelay(true);

+      sock.setTrafficClass(0x10);

+      setServerName(serverHost);

+      jis = new JavaInStream(sock.getInputStream());

+      jos = new JavaOutStream(sock.getOutputStream());

+    } catch (java.net.SocketException e) {

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

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

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

+    }

+    setStreams(jis, jos);

+    initialiseProtocol();

+  }

+

+  public boolean showMsgBox(int flags, String title, String text)

+  {

+    StringBuffer titleText = new StringBuffer("VNC Viewer: "+title);

+

+    return true;

+  }

+

+  // deleteWindow() is called when the user closes the desktop or menu windows.

+

+  void deleteWindow() {

+    if (viewport != null)

+      viewport.dispose();

+    viewport = null;

+  } 

+

+  // getUserPasswd() is called by the CSecurity object when it needs us to read

+  // a password from the user.

+

+  public final boolean getUserPasswd(StringBuffer user, StringBuffer passwd) {

+    String title = ("VNC Authentication ["

+                    +csecurity.description() + "]");

+    PasswdDialog dlg;    

+    if (user == null) {

+      dlg = new PasswdDialog(title, (user == null), (passwd == null));

+    } else {

+      if ((passwd == null) && viewer.sendLocalUsername.getValue()) {

+         user.append((String)System.getProperties().get("user.name"));

+         return true;

+      }

+      dlg = new PasswdDialog(title, viewer.sendLocalUsername.getValue(), 

+         (passwd == null));

+    }

+    if (!dlg.showDialog()) return false;

+    if (user != null) {

+      if (viewer.sendLocalUsername.getValue()) {

+         user.append((String)System.getProperties().get("user.name"));

+      } else {

+         user.append(dlg.userEntry.getText());

+      }

+    }

+    if (passwd != null)

+      passwd.append(dlg.passwdEntry.getText());

+    return true;

+  }

+

+  // CConnection callback methods

+

+  // serverInit() is called when the serverInit message has been received.  At

+  // this point we create the desktop window and display it.  We also tell the

+  // server the pixel format and encodings to use and request the first update.

+  public void serverInit() {

+    super.serverInit();

+

+    // If using AutoSelect with old servers, start in FullColor

+    // mode. See comment in autoSelectFormatAndEncoding. 

+    if (cp.beforeVersion(3, 8) && autoSelect) {

+      fullColour = true;

+    }

+

+    serverPF = cp.pf();

+    desktop = new DesktopWindow(cp.width, cp.height, serverPF, this);

+    //desktopEventHandler = desktop.setEventHandler(this);

+    //desktop.addEventMask(KeyPressMask | KeyReleaseMask);

+    fullColourPF = desktop.getPF();

+    if (!serverPF.trueColour)

+      fullColour = true;

+    recreateViewport();

+    formatChange = true; encodingChange = true;

+    requestNewUpdate();

+  }

+

+  // setDesktopSize() is called when the desktop size changes (including when

+  // it is set initially).

+  public void setDesktopSize(int w, int h) {

+    super.setDesktopSize(w,h);

+    resizeFramebuffer();

+  }

+

+  // setExtendedDesktopSize() is a more advanced version of setDesktopSize()

+  public void setExtendedDesktopSize(int reason, int result, int w, int h,

+                                     ScreenSet layout) {

+    super.setExtendedDesktopSize(reason, result, w, h, layout);

+

+    if ((reason == screenTypes.reasonClient) &&

+        (result != screenTypes.resultSuccess)) {

+      vlog.error("SetDesktopSize failed: "+result);

+      return;

+    }

+    

+    resizeFramebuffer();

+  }

+

+  // setName() is called when the desktop name changes

+  public void setName(String name) {

+    super.setName(name);

+  

+    if (viewport != null) {

+      viewport.setTitle("TigerVNC: "+name);

+    }

+  }

+

+  // framebufferUpdateStart() is called at the beginning of an update.

+  // Here we try to send out a new framebuffer update request so that the

+  // next update can be sent out in parallel with us decoding the current

+  // one. We cannot do this if we're in the middle of a format change

+  // though.

+  public void framebufferUpdateStart() {

+    if (!formatChange) {

+      pendingUpdate = true;

+      requestNewUpdate();

+    } else 

+      pendingUpdate = false;

+  }

+

+  // framebufferUpdateEnd() is called at the end of an update.

+  // For each rectangle, the FdInStream will have timed the speed

+  // of the connection, allowing us to select format and encoding

+  // appropriately, and then request another incremental update.

+  public void framebufferUpdateEnd() {

+    desktop.framebufferUpdateEnd();

+

+    if (firstUpdate) {

+      //int width = (int)viewer.desktopSize.getValue().split("x")[0];

+      //int height = (int)viewer.desktopSize.getValue().split("x")[1];

+      

+/*

+      if (cp.supportsSetDesktopSize &&

+          ((width != 0) && (height != 0))) {

+        ScreenSet layout;

+

+        layout = cp.screenLayout;

+

+        if (layout.num_screens() == 0)

+          layout.add_screen(new Screen());

+        else if (layout.num_screens() != 1) {

+

+          while (true) {

+            Iterator iter = layout.screens.iterator(); 

+            iter.next();

+        

+            if (!iter.hasNext())

+              break;

+

+            layout.remove_screen(iter.id);

+          }

+        }

+

+        layout.screens.iterator().dimensions.tl.x = 0;

+        layout.screens.iterator().dimensions.tl.y = 0;

+        layout.screens.iterator().dimensions.by.x = width;

+        layout.screens.iterator().dimensions.by.y = height;

+

+        writer().writeSetDesktopSize(width, height, layout);

+      }

+*/

+

+      firstUpdate = false;

+    }

+

+    // A format change prevented us from sending this before the update,

+    // so make sure to send it now.

+    if (formatChange && !pendingUpdate)

+      requestNewUpdate();

+

+    // Compute new settings based on updated bandwidth values

+    if (autoSelect)

+      autoSelectFormatAndEncoding();

+  }

+

+  // The rest of the callbacks are fairly self-explanatory...

+

+  public void setColourMapEntries(int firstColour, int nColours, int[] rgbs) {

+    desktop.setColourMapEntries(firstColour, nColours, rgbs);

+  }

+

+  public void bell() { desktop.getToolkit().beep(); }

+

+  public void serverCutText(String str, int len) {

+    if (viewer.acceptClipboard.getValue())

+      clipboardDialog.serverCutText(str, len);

+  }

+

+  // We start timing on beginRect and stop timing on endRect, to

+  // avoid skewing the bandwidth estimation as a result of the server

+  // being slow or the network having high latency

+  public void beginRect(Rect r, int encoding) {

+    ((JavaInStream)getInStream()).startTiming();

+    if (encoding != Encodings.encodingCopyRect) {

+      lastServerEncoding = encoding;

+    }

+  }

+

+  public void endRect(Rect r, int encoding) {

+    ((JavaInStream)getInStream()).stopTiming();

+  }

+

+  public void fillRect(Rect r, int p) {

+    desktop.fillRect(r.tl.x, r.tl.y, r.width(), r.height(), p);

+  }

+  public void imageRect(Rect r, int[] p) {

+    desktop.imageRect(r.tl.x, r.tl.y, r.width(), r.height(), p);

+  }

+  public void copyRect(Rect r, int sx, int sy) {

+    desktop.copyRect(r.tl.x, r.tl.y, r.width(), r.height(), sx, sy);

+  }

+

+  public void setCursor(int width, int height, Point hotspot,

+                        int[] data, byte[] mask) {

+    desktop.setCursor(width, height, hotspot, data, mask);

+  }

+

+  private void resizeFramebuffer()

+  {

+    if (desktop == null)

+      return;

+    if ((desktop.width() == cp.width) && (desktop.height() == cp.height))

+      return;

+    

+    desktop.resize();

+    recreateViewport();

+  }

+

+  // recreateViewport() recreates our top-level window.  This seems to be

+  // better than attempting to resize the existing window, at least with

+  // various X window managers.

+

+  private void recreateViewport()

+  {

+    if (viewport != null) viewport.dispose();

+    viewport = new ViewportFrame(cp.name(), this);

+    viewport.setUndecorated(fullScreen);

+    ClassLoader loader = this.getClass().getClassLoader();

+    URL url = loader.getResource("tigervnc.ico");

+    ImageIcon icon = null;

+    if (url != null) {

+      icon = new ImageIcon(url);

+      viewport.setIconImage(icon.getImage());

+    }

+    viewport.addChild(desktop);

+    reconfigureViewport();

+    viewport.setVisible(true);

+    desktop.initGraphics();

+    desktop.requestFocusInWindow();

+  }

+

+  private void reconfigureViewport()

+  {

+    //viewport->setMaxSize(cp.width, cp.height);

+    int w = cp.width;

+    int h = cp.height;

+    Dimension dpySize = viewport.getToolkit().getScreenSize();

+    int wmDecorationWidth = 0;

+    int wmDecorationHeight = 24;

+    if (w + wmDecorationWidth >= dpySize.width)

+      w = dpySize.width - wmDecorationWidth;

+    if (h + wmDecorationHeight >= dpySize.height)

+      h = dpySize.height - wmDecorationHeight;

+

+    int x = (dpySize.width - w - wmDecorationWidth) / 2;

+    int y = (dpySize.height - h - wmDecorationHeight)/2;

+

+    if (fullScreen) {

+      viewport.setExtendedState(JFrame.MAXIMIZED_BOTH);

+      viewport.setGeometry(0, 0, dpySize.width, dpySize.height);

+    } else {

+      viewport.setExtendedState(JFrame.NORMAL);

+      viewport.setGeometry(x, y, w, h);

+    }

+  }

+

+  // autoSelectFormatAndEncoding() chooses the format and encoding appropriate

+  // to the connection speed:

+  //

+  //   First we wait for at least one second of bandwidth measurement.

+  //

+  //   Above 16Mbps (i.e. LAN), we choose the second highest JPEG quality,

+  //   which should be perceptually lossless.

+  //

+  //   If the bandwidth is below that, we choose a more lossy JPEG quality.

+  //

+  //   If the bandwidth drops below 256 Kbps, we switch to palette mode.

+  //

+  //   Note: The system here is fairly arbitrary and should be replaced

+  //         with something more intelligent at the server end.

+  //

+  private void autoSelectFormatAndEncoding() {

+    long kbitsPerSecond = ((JavaInStream)getInStream()).kbitsPerSecond();

+    long timeWaited = ((JavaInStream)getInStream()).timeWaited();

+    boolean newFullColour = fullColour;

+    int newQualityLevel = cp.qualityLevel;

+

+    // Always use Tight

+    if (currentEncoding != Encodings.encodingTight) {

+      currentEncoding = Encodings.encodingTight;

+      encodingChange = true;

+    }

+

+    // Check that we have a decent bandwidth measurement

+    if ((kbitsPerSecond == 0) || (timeWaited < 10000))

+      return;

+  

+    // Select appropriate quality level

+    if (!cp.noJpeg) {

+      if (kbitsPerSecond > 16000)

+        newQualityLevel = 8;

+      else

+        newQualityLevel = 6;

+  

+      if (newQualityLevel != cp.qualityLevel) {

+        vlog.info("Throughput "+kbitsPerSecond+

+                  " kbit/s - changing to quality "+newQualityLevel);

+        cp.qualityLevel = newQualityLevel;

+        viewer.qualityLevel.setParam(Integer.toString(newQualityLevel));

+        encodingChange = true;

+      }

+    }

+

+    if (cp.beforeVersion(3, 8)) {

+      // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with

+      // cursors "asynchronously". If this happens in the middle of a

+      // pixel format change, the server will encode the cursor with

+      // the old format, but the client will try to decode it

+      // according to the new format. This will lead to a

+      // crash. Therefore, we do not allow automatic format change for

+      // old servers.

+      return;

+    }

+    

+    // Select best color level

+    newFullColour = (kbitsPerSecond > 256);

+    if (newFullColour != fullColour) {

+      vlog.info("Throughput "+kbitsPerSecond+

+                " kbit/s - full color is now "+ 

+  	            (newFullColour ? "enabled" : "disabled"));

+      fullColour = newFullColour;

+      formatChange = true;

+    } 

+  }

+

+  // requestNewUpdate() requests an update from the server, having set the

+  // format and encoding appropriately.

+  private void requestNewUpdate()

+  {

+    if (formatChange) {

+      if (fullColour) {

+        desktop.setPF(fullColourPF);

+      } else {

+        if (lowColourLevel == 0) {

+          desktop.setPF(new PixelFormat(8,3,false,true,1,1,1,2,1,0));

+        } else if (lowColourLevel == 1) {

+          desktop.setPF(new PixelFormat(8,6,false,true,3,3,3,4,2,0));

+        } else {

+          desktop.setPF(new PixelFormat(8,8,false,true,7,7,3,0,3,6));

+        }

+      }

+      String str = desktop.getPF().print();

+      vlog.info("Using pixel format "+str);

+      cp.setPF(desktop.getPF());

+      synchronized (this) {

+        writer().writeSetPixelFormat(cp.pf());

+      }

+    }

+    checkEncodings();

+    synchronized (this) {

+      writer().writeFramebufferUpdateRequest(new Rect(0,0,cp.width,cp.height),

+                                             !formatChange);

+    }

+    formatChange = false;

+  }

+

+

+  ////////////////////////////////////////////////////////////////////

+  // The following methods are all called from the GUI thread

+

+  // close() closes the socket, thus waking up the RFB thread.

+  public void close() {

+    try {

+      shuttingDown = true;

+      sock.close();

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

+      e.printStackTrace();

+    }

+  }

+

+  // Menu callbacks.  These are guaranteed only to be called after serverInit()

+  // has been called, since the menu is only accessible from the DesktopWindow

+

+  private void initMenu() {

+    menu = new F8Menu(this);

+  }

+

+  void showMenu(int x, int y) {

+    String os = System.getProperty("os.name");

+    if (os.startsWith("Windows"))

+      com.sun.java.swing.plaf.windows.WindowsLookAndFeel.setMnemonicHidden(false);

+    menu.show(desktop, x, y);

+  }

+

+  void showAbout() {

+    InputStream stream = cl.getResourceAsStream("manifest");

+    String pkgVersion = "";

+    String pkgDate = "";

+    String pkgTime = "";

+    try {

+      Manifest manifest = new Manifest(stream);

+      Attributes attributes = manifest.getMainAttributes();

+      pkgVersion = attributes.getValue("Package-Version");

+      pkgDate = attributes.getValue("Package-Date");

+      pkgTime = attributes.getValue("Package-Time");

+    } catch (IOException e) { }

+    JOptionPane.showMessageDialog((viewport != null ? viewport : null),

+      VncViewer.about1+"\n"

+      +"Built on "+pkgDate+" at "+pkgTime+"\n"

+      +VncViewer.about2+"\n"

+      +VncViewer.about3,

+      "About TigerVNC Viewer for Java",

+      JOptionPane.INFORMATION_MESSAGE,

+      logo);

+  }

+

+  void showInfo() {

+    JOptionPane.showMessageDialog(viewport,

+      "Desktop name: "+cp.name()+"\n"

+      +"Host: "+serverHost+":"+sock.getPort()+"\n"

+      +"Size: "+cp.width+"x"+cp.height+"\n"

+      +"Pixel format: "+desktop.getPF().print()+"\n"

+      +"(server default "+serverPF.print()+")\n"

+      +"Requested encoding: "+Encodings.encodingName(currentEncoding)+"\n"

+      +"Last used encoding: "+Encodings.encodingName(lastServerEncoding)+"\n"

+      +"Line speed estimate: "+((JavaInStream)getInStream()).kbitsPerSecond()+" kbit/s"+"\n"

+      +"Protocol version: "+cp.majorVersion+"."+cp.minorVersion+"\n"

+      +"Security method: "+Security.secTypeName(csecurity.getType())

+       +" ["+csecurity.description()+"]",

+      "VNC connection info",

+      JOptionPane.PLAIN_MESSAGE);

+  }

+

+  synchronized public void refresh() {

+    writer().writeFramebufferUpdateRequest(new Rect(0,0,cp.width,cp.height), false);

+  }

+

+

+  // OptionsDialogCallback.  setOptions() sets the options dialog's checkboxes

+  // etc to reflect our flags.  getOptions() sets our flags according to the

+  // options dialog's checkboxes.  They are both called from the GUI thread.

+  // Some of the flags are also accessed by the RFB thread.  I believe that

+  // reading and writing boolean and int values in java is atomic, so there is

+  // no need for synchronization.

+

+  public void setOptions() {

+    int digit;

+    options.autoSelect.setSelected(autoSelect);

+    options.fullColour.setSelected(fullColour);

+    options.veryLowColour.setSelected(!fullColour && lowColourLevel == 0);

+    options.lowColour.setSelected(!fullColour && lowColourLevel == 1);

+    options.mediumColour.setSelected(!fullColour && lowColourLevel == 2);

+    options.tight.setSelected(currentEncoding == Encodings.encodingTight);

+    options.zrle.setSelected(currentEncoding == Encodings.encodingZRLE);

+    options.hextile.setSelected(currentEncoding == Encodings.encodingHextile);

+    options.raw.setSelected(currentEncoding == Encodings.encodingRaw);

+

+    options.customCompressLevel.setSelected(viewer.customCompressLevel.getValue());

+    digit = -1 + viewer.compressLevel.getValue();

+    options.compressLevel.setSelectedIndex(digit);

+    options.noJpeg.setSelected(!viewer.noJpeg.getValue());

+    digit = -1 + viewer.qualityLevel.getValue();

+    options.qualityLevel.setSelectedIndex(digit);

+

+    options.viewOnly.setSelected(viewer.viewOnly.getValue());

+    options.acceptClipboard.setSelected(viewer.acceptClipboard.getValue());

+    options.sendClipboard.setSelected(viewer.sendClipboard.getValue());

+    options.menuKey.setSelectedIndex(menuKey-0xFFBE);

+

+    if (state() == RFBSTATE_NORMAL) {

+      options.shared.setEnabled(false);

+      options.secVeNCrypt.setEnabled(false);

+      options.encNone.setEnabled(false);

+      options.encTLS.setEnabled(false);

+      options.encX509.setEnabled(false);

+      options.ca.setEnabled(false);

+      options.crl.setEnabled(false);

+      options.secManaged.setEnabled(false);

+      options.secNone.setEnabled(false);

+      options.secVnc.setEnabled(false);

+      options.secPlain.setEnabled(false);

+      options.sendLocalUsername.setEnabled(false);

+    } else {

+      options.shared.setSelected(shared);

+

+      /* Process non-VeNCrypt sectypes */

+      java.util.List<Integer> secTypes = new ArrayList<Integer>();

+      secTypes = security.GetEnabledSecTypes();

+      for (Iterator i = secTypes.iterator(); i.hasNext();) {

+        switch ((Integer)i.next()) {

+        case Security.secTypeVeNCrypt:

+          options.secVeNCrypt.setSelected(true);

+          break;

+        case Security.secTypeManaged:

+          options.encNone.setSelected(true);

+          options.secManaged.setSelected(true);

+          options.sendLocalUsername.setSelected(true);

+          break;

+        case Security.secTypeNone:

+          options.encNone.setSelected(true);

+          options.secNone.setSelected(true);

+          break;

+        case Security.secTypeVncAuth:

+          options.encNone.setSelected(true);

+          options.secVnc.setSelected(true);

+          break;

+        }

+      }

+

+      /* Process VeNCrypt subtypes */

+      if (options.secVeNCrypt.isSelected()) {

+        java.util.List<Integer> secTypesExt = new ArrayList<Integer>();

+        secTypesExt = security.GetEnabledExtSecTypes();

+        for (Iterator iext = secTypesExt.iterator(); iext.hasNext();) {

+          switch ((Integer)iext.next()) {

+          case Security.secTypePlain:

+            options.secPlain.setSelected(true);

+            options.sendLocalUsername.setSelected(true);

+            break;

+          case Security.secTypeTLSNone:

+            options.encTLS.setSelected(true);

+            options.secNone.setSelected(true);

+            break;

+          case Security.secTypeTLSVnc:

+            options.encTLS.setSelected(true);

+            options.secVnc.setSelected(true);

+            break;

+          case Security.secTypeTLSPlain:

+            options.encTLS.setSelected(true);

+            options.secPlain.setSelected(true);

+            options.sendLocalUsername.setSelected(true);

+            break;

+          case Security.secTypeX509None:

+            options.encX509.setSelected(true);

+            options.secNone.setSelected(true);

+            break;

+          case Security.secTypeX509Vnc:

+            options.encX509.setSelected(true);

+            options.secVnc.setSelected(true);

+            break;

+          case Security.secTypeX509Plain:

+            options.encX509.setSelected(true);

+            options.secPlain.setSelected(true);

+            options.sendLocalUsername.setSelected(true);

+            break;

+          }

+        }

+      }

+      options.sendLocalUsername.setEnabled(options.secPlain.isSelected()||

+        options.secManaged.isSelected());

+    }

+

+    options.fullScreen.setSelected(fullScreen);

+    options.useLocalCursor.setSelected(viewer.useLocalCursor.getValue());

+    options.fastCopyRect.setSelected(viewer.fastCopyRect.getValue());

+  }

+

+  public void getOptions() {

+    autoSelect = options.autoSelect.isSelected();

+    if (fullColour != options.fullColour.isSelected())

+      formatChange = true;

+    fullColour = options.fullColour.isSelected();

+    if (!fullColour) {

+      int newLowColourLevel = (options.veryLowColour.isSelected() ? 0 :

+                               options.lowColour.isSelected() ? 1 : 2);

+      if (newLowColourLevel != lowColourLevel) {

+        lowColourLevel = newLowColourLevel;

+        formatChange = true;

+      }

+    }

+    int newEncoding = (options.zrle.isSelected() ?  Encodings.encodingZRLE :

+                       options.hextile.isSelected() ?  Encodings.encodingHextile :

+                       options.tight.isSelected() ?  Encodings.encodingTight :

+                       Encodings.encodingRaw);

+    if (newEncoding != currentEncoding) {

+      currentEncoding = newEncoding;

+      encodingChange = true;

+    }

+

+    viewer.customCompressLevel.setParam(options.customCompressLevel.isSelected());

+    if (cp.customCompressLevel != viewer.customCompressLevel.getValue()) {

+      cp.customCompressLevel = viewer.customCompressLevel.getValue();

+      encodingChange = true;

+    }

+    viewer.compressLevel.setParam(Integer.toString(options.compressLevel.getSelectedIndex()));

+    if (cp.compressLevel != viewer.compressLevel.getValue()) {

+      cp.compressLevel = viewer.compressLevel.getValue();

+      encodingChange = true;

+    }

+    viewer.noJpeg.setParam(!options.noJpeg.isSelected());

+    if (cp.noJpeg != viewer.noJpeg.getValue()) {

+      cp.noJpeg = viewer.noJpeg.getValue();

+      encodingChange = true;

+    }

+    viewer.qualityLevel.setParam(Integer.toString(options.qualityLevel.getSelectedIndex()));

+    if (cp.qualityLevel != viewer.qualityLevel.getValue()) {

+      cp.qualityLevel = viewer.qualityLevel.getValue();

+      encodingChange = true;

+    }

+    viewer.sendLocalUsername.setParam(options.sendLocalUsername.isSelected());

+

+    viewer.viewOnly.setParam(options.viewOnly.isSelected());

+    viewer.acceptClipboard.setParam(options.acceptClipboard.isSelected());

+    viewer.sendClipboard.setParam(options.sendClipboard.isSelected());

+    clipboardDialog.setSendingEnabled(viewer.sendClipboard.getValue());

+    menuKey = (int)(options.menuKey.getSelectedIndex()+0xFFBE);

+    F8Menu.f8.setLabel("Send F"+(menuKey-Keysyms.F1+1));

+

+    shared = options.shared.isSelected();

+    setShared(shared);

+    viewer.useLocalCursor.setParam(options.useLocalCursor.isSelected());

+    if (cp.supportsLocalCursor != viewer.useLocalCursor.getValue()) {

+      cp.supportsLocalCursor = viewer.useLocalCursor.getValue();

+      encodingChange = true;

+      if (desktop != null)

+        desktop.resetLocalCursor();

+    }

+    

+    checkEncodings();

+  

+    if (state() != RFBSTATE_NORMAL) {

+      /* Process security types which don't use encryption */

+      if (options.encNone.isSelected()) {

+        if (options.secManaged.isSelected())

+          Security.EnableSecType(Security.secTypeManaged);

+        if (options.secNone.isSelected())

+          Security.EnableSecType(Security.secTypeNone);

+        if (options.secVnc.isSelected())

+          Security.EnableSecType(Security.secTypeVncAuth);

+        if (options.secPlain.isSelected())

+          Security.EnableSecType(Security.secTypePlain);

+      } else {

+        Security.DisableSecType(Security.secTypeManaged);

+        Security.DisableSecType(Security.secTypeNone);

+        Security.DisableSecType(Security.secTypeVncAuth);

+        Security.DisableSecType(Security.secTypePlain);

+      }

+

+      /* Process security types which use TLS encryption */

+      if (options.encTLS.isSelected()) {

+        if (options.secNone.isSelected())

+          Security.EnableSecType(Security.secTypeTLSNone);

+        if (options.secVnc.isSelected())

+          Security.EnableSecType(Security.secTypeTLSVnc);

+        if (options.secPlain.isSelected())

+          Security.EnableSecType(Security.secTypeTLSPlain);

+      } else {

+        Security.DisableSecType(Security.secTypeTLSNone);

+        Security.DisableSecType(Security.secTypeTLSVnc);

+        Security.DisableSecType(Security.secTypeTLSPlain);

+      }

+  

+      /* Process security types which use X509 encryption */

+      if (options.encX509.isSelected()) {

+        if (options.secNone.isSelected())

+          Security.EnableSecType(Security.secTypeX509None);

+        if (options.secVnc.isSelected())

+          Security.EnableSecType(Security.secTypeX509Vnc);

+        if (options.secPlain.isSelected())

+          Security.EnableSecType(Security.secTypeX509Plain);

+      } else {

+        Security.DisableSecType(Security.secTypeX509None);

+        Security.DisableSecType(Security.secTypeX509Vnc);

+        Security.DisableSecType(Security.secTypeX509Plain);

+      }

+  

+      /* Process *None security types */

+      if (options.secNone.isSelected()) {

+        if (options.encNone.isSelected())

+          Security.EnableSecType(Security.secTypeNone);

+        if (options.encTLS.isSelected())

+          Security.EnableSecType(Security.secTypeTLSNone);

+        if (options.encX509.isSelected())

+          Security.EnableSecType(Security.secTypeX509None);

+      } else {

+        Security.DisableSecType(Security.secTypeNone);

+        Security.DisableSecType(Security.secTypeTLSNone);

+        Security.DisableSecType(Security.secTypeX509None);

+      }

+  

+      /* Process *Vnc security types */

+      if (options.secVnc.isSelected()) {

+        if (options.encNone.isSelected())

+          Security.EnableSecType(Security.secTypeVncAuth);

+        if (options.encTLS.isSelected())

+          Security.EnableSecType(Security.secTypeTLSVnc);

+        if (options.encX509.isSelected())

+          Security.EnableSecType(Security.secTypeX509Vnc);

+      } else {

+        Security.DisableSecType(Security.secTypeVncAuth);

+        Security.DisableSecType(Security.secTypeTLSVnc);

+        Security.DisableSecType(Security.secTypeX509Vnc);

+      }

+  

+      /* Process *Plain security types */

+      if (options.secPlain.isSelected()) {

+        if (options.encNone.isSelected())

+          Security.EnableSecType(Security.secTypePlain);

+        if (options.encTLS.isSelected())

+          Security.EnableSecType(Security.secTypeTLSPlain);

+        if (options.encX509.isSelected())

+          Security.EnableSecType(Security.secTypeX509Plain);

+      } else {

+        Security.DisableSecType(Security.secTypePlain);

+        Security.DisableSecType(Security.secTypeTLSPlain);

+        Security.DisableSecType(Security.secTypeX509Plain);

+      }

+  

+      CSecurityTLS.x509ca.setParam(options.ca.getText());

+      CSecurityTLS.x509crl.setParam(options.crl.getText());

+    }

+  }

+

+  public void toggleFullScreen() {

+    fullScreen = !fullScreen;

+    if (!fullScreen) menu.fullScreen.setSelected(false);

+    recreateViewport();

+  }

+

+  // writeClientCutText() is called from the clipboard dialog

+  synchronized public void writeClientCutText(String str, int len) {

+    if (state() != RFBSTATE_NORMAL) return;

+    writer().writeClientCutText(str,len);

+  }

+

+  synchronized public void writeKeyEvent(int keysym, boolean down) {

+    if (state() != RFBSTATE_NORMAL) return;

+    writer().writeKeyEvent(keysym, down);

+  }

+

+  synchronized public void writeKeyEvent(KeyEvent ev) {

+    if (ev.getID() != KeyEvent.KEY_PRESSED && !ev.isActionKey())

+      return;

+

+    int keysym;

+

+    if (!ev.isActionKey()) {

+      vlog.debug("key press "+ev.getKeyChar());

+      if (ev.getKeyChar() < 32) {

+        // if the ctrl modifier key is down, send the equivalent ASCII since we

+        // will send the ctrl modifier anyway

+

+        if ((ev.getModifiers() & KeyEvent.CTRL_MASK) != 0) {

+          if ((ev.getModifiers() & KeyEvent.SHIFT_MASK) != 0) {

+            keysym = ev.getKeyChar() + 64;

+            if (keysym == -1)

+              return;

+          } else { 

+            keysym = ev.getKeyChar() + 96;

+            if (keysym == 127) keysym = 95;

+          }

+        } else {

+          switch (ev.getKeyCode()) {

+          case KeyEvent.VK_BACK_SPACE: keysym = Keysyms.BackSpace; break;

+          case KeyEvent.VK_TAB:        keysym = Keysyms.Tab; break;

+          case KeyEvent.VK_ENTER:      keysym = Keysyms.Return; break;

+          case KeyEvent.VK_ESCAPE:     keysym = Keysyms.Escape; break;

+          default: return;

+          }

+        }

+

+      } else if (ev.getKeyChar() == 127) {

+        keysym = Keysyms.Delete;

+

+      } else {

+        keysym = UnicodeToKeysym.translate(ev.getKeyChar());

+        if (keysym == -1)

+          return;

+      }

+

+    } else {

+      // KEY_ACTION

+      vlog.debug("key action "+ev.getKeyCode());

+      switch (ev.getKeyCode()) {

+      case KeyEvent.VK_HOME:         keysym = Keysyms.Home; break;

+      case KeyEvent.VK_END:          keysym = Keysyms.End; break;

+      case KeyEvent.VK_PAGE_UP:      keysym = Keysyms.Page_Up; break;

+      case KeyEvent.VK_PAGE_DOWN:    keysym = Keysyms.Page_Down; break;

+      case KeyEvent.VK_UP:           keysym = Keysyms.Up; break;

+      case KeyEvent.VK_DOWN:         keysym = Keysyms.Down; break;

+      case KeyEvent.VK_LEFT:         keysym = Keysyms.Left; break;

+      case KeyEvent.VK_RIGHT:        keysym = Keysyms.Right; break;

+      case KeyEvent.VK_F1:           keysym = Keysyms.F1; break;

+      case KeyEvent.VK_F2:           keysym = Keysyms.F2; break;

+      case KeyEvent.VK_F3:           keysym = Keysyms.F3; break;

+      case KeyEvent.VK_F4:           keysym = Keysyms.F4; break;

+      case KeyEvent.VK_F5:           keysym = Keysyms.F5; break;

+      case KeyEvent.VK_F6:           keysym = Keysyms.F6; break;

+      case KeyEvent.VK_F7:           keysym = Keysyms.F7; break;

+      case KeyEvent.VK_F8:           keysym = Keysyms.F8; break;

+      case KeyEvent.VK_F9:           keysym = Keysyms.F9; break;

+      case KeyEvent.VK_F10:          keysym = Keysyms.F10; break;

+      case KeyEvent.VK_F11:          keysym = Keysyms.F11; break;

+      case KeyEvent.VK_F12:          keysym = Keysyms.F12; break;

+      case KeyEvent.VK_PRINTSCREEN:  keysym = Keysyms.Print; break;

+      case KeyEvent.VK_PAUSE:        keysym = Keysyms.Pause; break;

+      case KeyEvent.VK_INSERT:       keysym = Keysyms.Insert; break;

+      default: return;

+      }

+    }

+

+    writeModifiers(ev.getModifiers());

+    writeKeyEvent(keysym, true);

+    writeKeyEvent(keysym, false);

+    writeModifiers(0);

+  }

+

+

+  synchronized public void writePointerEvent(MouseEvent ev) {

+    if (state() != RFBSTATE_NORMAL) return;

+    int x, y;

+

+    switch (ev.getID()) {

+    case MouseEvent.MOUSE_PRESSED:

+      buttonMask = 1;

+      if ((ev.getModifiers() & KeyEvent.ALT_MASK) != 0) buttonMask = 2;

+      if ((ev.getModifiers() & KeyEvent.META_MASK) != 0) buttonMask = 4;

+      break;

+    case MouseEvent.MOUSE_RELEASED:

+      buttonMask = 0;

+      break;

+    }

+

+    writeModifiers(ev.getModifiers() & ~KeyEvent.ALT_MASK & ~KeyEvent.META_MASK);

+

+    x = ev.getX();

+    y = ev.getY();

+    if (x < 0) x = 0;

+    if (x > cp.width-1) x = cp.width-1;

+    if (y < 0) y = 0;

+    if (y > cp.height-1) y = cp.height-1;

+

+    writer().writePointerEvent(new Point(x, y), buttonMask);

+

+    if (buttonMask == 0) writeModifiers(0);

+  }

+

+

+  synchronized public void writeWheelEvent(MouseWheelEvent ev) {

+    if (state() != RFBSTATE_NORMAL) return;

+    int x, y;

+    int clicks = ev.getWheelRotation();

+    if (clicks < 0) {

+      buttonMask = 8;

+    } else {

+      buttonMask = 16;

+    }

+    writeModifiers(ev.getModifiers() & ~KeyEvent.ALT_MASK & ~KeyEvent.META_MASK);

+    for (int i=0;i<java.lang.Math.abs(clicks);i++) {

+      x = ev.getX();

+      y = ev.getY();

+      writer().writePointerEvent(new Point(x, y), buttonMask);

+      buttonMask = 0;

+      writer().writePointerEvent(new Point(x, y), buttonMask);

+    }

+    writeModifiers(0);

+

+  }

+

+

+  void writeModifiers(int m) {

+    if ((m & Event.SHIFT_MASK) != (pressedModifiers & Event.SHIFT_MASK))

+      writeKeyEvent(Keysyms.Shift_L, (m & Event.SHIFT_MASK) != 0);

+    if ((m & Event.CTRL_MASK) != (pressedModifiers & Event.CTRL_MASK))

+      writeKeyEvent(Keysyms.Control_L, (m & Event.CTRL_MASK) != 0);

+    if ((m & Event.ALT_MASK) != (pressedModifiers & Event.ALT_MASK))

+      writeKeyEvent(Keysyms.Alt_L, (m & Event.ALT_MASK) != 0);

+    if ((m & Event.META_MASK) != (pressedModifiers & Event.META_MASK))

+      writeKeyEvent(Keysyms.Meta_L, (m & Event.META_MASK) != 0);

+    pressedModifiers = m;

+  }

+

+

+  ////////////////////////////////////////////////////////////////////

+  // The following methods are called from both RFB and GUI threads

+

+  // checkEncodings() sends a setEncodings message if one is needed.

+  synchronized private void checkEncodings() {

+    if (encodingChange && state() == RFBSTATE_NORMAL) {

+      vlog.info("Using "+Encodings.encodingName(currentEncoding)+" encoding");

+      writer().writeSetEncodings(currentEncoding, true);

+      encodingChange = false;

+    }

+  }

+

+  // the following never change so need no synchronization:

+  JavaInStream jis;

+  JavaOutStream jos;

+

+

+  // viewer object is only ever accessed by the GUI thread so needs no

+  // synchronization (except for one test in DesktopWindow - see comment

+  // there).

+  VncViewer viewer;

+

+  // access to desktop by different threads is specified in DesktopWindow

+

+  // the following need no synchronization:

+

+  ClassLoader cl = this.getClass().getClassLoader();

+  ImageIcon logo = new ImageIcon(cl.getResource("com/tigervnc/vncviewer/tigervnc.png"));

+  public static UserPasswdGetter upg;

+  public UserMsgBox msg;

+

+  // shuttingDown is set by the GUI thread and only ever tested by the RFB

+  // thread after the window has been destroyed.

+  boolean shuttingDown;

+

+  // reading and writing int and boolean is atomic in java, so no

+  // synchronization of the following flags is needed:

+  int currentEncoding, lastServerEncoding;

+  

+  int lowColourLevel;

+

+

+  // All menu, options, about and info stuff is done in the GUI thread (apart

+  // from when constructed).

+  F8Menu menu;

+  OptionsDialog options;

+

+  // clipboard sync issues?

+  ClipboardDialog clipboardDialog;

+

+  // the following are only ever accessed by the GUI thread:

+  int buttonMask;

+  int pressedModifiers;

+

+  public String serverHost;

+  public int serverPort;

+  public int menuKey;

+  PixelFormat serverPF;

+  ViewportFrame viewport;

+  DesktopWindow desktop;

+  PixelFormat fullColourPF;

+  boolean fullColour;

+  boolean autoSelect;

+  boolean shared;

+  boolean formatChange;

+  boolean encodingChange;

+  boolean sameMachine;

+  boolean fullScreen;

+  boolean reverseConnection;

+  boolean firstUpdate;

+  boolean pendingUpdate;

+  

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

+}

diff --git a/java/src/com/tigervnc/vncviewer/ClipboardDialog.java b/java/src/com/tigervnc/vncviewer/ClipboardDialog.java
new file mode 100644
index 0000000..9753b15
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/ClipboardDialog.java
@@ -0,0 +1,107 @@
+/* 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.vncviewer;

+

+import java.awt.*;

+import java.awt.event.*;

+import java.awt.datatransfer.Clipboard;

+import java.awt.datatransfer.StringSelection;

+import javax.swing.*;

+import com.tigervnc.rfb.LogWriter;

+

+class ClipboardDialog extends Dialog implements ActionListener {

+

+  public ClipboardDialog(CConn cc_) {

+    super(false);

+    cc = cc_;

+    setTitle("VNC clipboard");

+    textArea = new JTextArea(5,50);

+    getContentPane().add("Center", textArea);

+

+    JPanel pb = new JPanel();

+    clearButton = new JButton("Clear");

+    pb.add(clearButton);

+    clearButton.addActionListener(this);

+    sendButton = new JButton("Send to VNC server");

+    pb.add(sendButton);

+    sendButton.addActionListener(this);

+    cancelButton = new JButton("Cancel");

+    pb.add(cancelButton);

+    cancelButton.addActionListener(this);

+    getContentPane().add("South", pb);

+

+    pack();

+  }

+

+  static Clipboard systemClipboard;

+  static {

+    try {

+      systemClipboard = Toolkit.getDefaultToolkit().getSystemClipboard();

+    } catch (Exception e) { }

+  }

+

+  public void initDialog() {

+    textArea.setText(current);

+    textArea.selectAll();

+  }

+  

+  public void setContents(String str) {

+    current = str;

+    textArea.setText(str);

+    textArea.selectAll();

+  }

+

+  public void serverCutText(String str, int len) {

+    setContents(str);    

+    if (systemClipboard != null) {

+      StringSelection ss = new StringSelection(str);

+      try {

+        systemClipboard.setContents(ss, ss);

+      } catch(Exception e) {

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

+      }

+    }

+  }

+

+  public void setSendingEnabled(boolean b) {

+    sendButton.setEnabled(b);

+  }

+

+  public void actionPerformed(ActionEvent e) {

+    Object s = e.getSource();

+    if (s instanceof JButton && (JButton)s == clearButton) {

+      current = "";

+      textArea.setText(current);

+    } else if (s instanceof JButton && (JButton)s == sendButton) {

+      ok = true;

+      current = textArea.getText();

+      cc.writeClientCutText(current, current.length());

+      endDialog();

+    } else if (s instanceof JButton && (JButton)s == cancelButton) {

+      ok = false;

+      endDialog();

+    }

+  }

+

+  CConn cc;

+  String current;

+  JTextArea textArea;

+  JButton clearButton, sendButton, cancelButton;

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

+}

diff --git a/java/src/com/tigervnc/vncviewer/ClipboardFrame.java b/java/src/com/tigervnc/vncviewer/ClipboardFrame.java
deleted file mode 100644
index a09519c..0000000
--- a/java/src/com/tigervnc/vncviewer/ClipboardFrame.java
+++ /dev/null
@@ -1,135 +0,0 @@
-//
-//  Copyright (C) 2001 HorizonLive.com, Inc.  All Rights Reserved.
-//  Copyright (C) 1999 AT&T Laboratories Cambridge.  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.
-//
-
-//
-// Clipboard frame.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.awt.*;
-import java.awt.event.*;
-
-class ClipboardFrame extends Frame
-  implements WindowListener, ActionListener {
-
-  TextArea textArea;
-  Button clearButton, closeButton;
-  String selection;
-  VncViewer viewer;
-
-  //
-  // Constructor.
-  //
-
-  ClipboardFrame(VncViewer v) {
-    super("TigerVNC Clipboard");
-
-    viewer = v;
-
-    GridBagLayout gridbag = new GridBagLayout();
-    setLayout(gridbag);
-
-    GridBagConstraints gbc = new GridBagConstraints();
-    gbc.gridwidth = GridBagConstraints.REMAINDER;
-    gbc.fill = GridBagConstraints.BOTH;
-    gbc.weighty = 1.0;
-
-    textArea = new TextArea(5, 40);
-    gridbag.setConstraints(textArea, gbc);
-    add(textArea);
-
-    gbc.fill = GridBagConstraints.HORIZONTAL;
-    gbc.weightx = 1.0;
-    gbc.weighty = 0.0;
-    gbc.gridwidth = 1;
-
-    clearButton = new Button("Clear");
-    gridbag.setConstraints(clearButton, gbc);
-    add(clearButton);
-    clearButton.addActionListener(this);
-
-    closeButton = new Button("Close");
-    gridbag.setConstraints(closeButton, gbc);
-    add(closeButton);
-    closeButton.addActionListener(this);
-
-    pack();
-
-    addWindowListener(this);
-  }
-
-
-  //
-  // Set the cut text from the RFB server.
-  //
-
-  void setCutText(String text) {
-    selection = text;
-    textArea.setText(text);
-    if (isVisible()) {
-      textArea.selectAll();
-    }
-  }
-
-
-  //
-  // When the focus leaves the window, see if we have new cut text and
-  // if so send it to the RFB server.
-  //
-
-  public void windowDeactivated (WindowEvent evt) {
-    if (selection != null && !selection.equals(textArea.getText())) {
-      selection = textArea.getText();
-      viewer.setCutText(selection);
-    }
-  }
-
-  //
-  // Close our window properly.
-  //
-
-  public void windowClosing(WindowEvent evt) {
-    setVisible(false);
-  }
-
-  //
-  // Ignore window events we're not interested in.
-  //
-
-  public void windowActivated(WindowEvent evt) {}
-  public void windowOpened(WindowEvent evt) {}
-  public void windowClosed(WindowEvent evt) {}
-  public void windowIconified(WindowEvent evt) {}
-  public void windowDeiconified(WindowEvent evt) {}
-
-
-  //
-  // Respond to button presses
-  //
-
-  public void actionPerformed(ActionEvent evt) {
-    if (evt.getSource() == clearButton) {
-      textArea.setText("");
-    } else if (evt.getSource() == closeButton) {
-      setVisible(false);
-    }
-  }
-}
diff --git a/java/src/com/tigervnc/vncviewer/DesktopWindow.java b/java/src/com/tigervnc/vncviewer/DesktopWindow.java
new file mode 100644
index 0000000..4fd80d3
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/DesktopWindow.java
@@ -0,0 +1,480 @@
+/* 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.

+ */

+
+//

+// DesktopWindow is an AWT Canvas representing a VNC desktop.

+//

+// Methods on DesktopWindow are called from both the GUI thread and the thread

+// which processes incoming RFB messages ("the RFB thread").  This means we

+// need to be careful with synchronization here.

+//

+

+package com.tigervnc.vncviewer;

+import java.awt.*;

+import java.awt.event.*;

+import java.awt.image.*;

+import java.awt.datatransfer.DataFlavor;

+import java.awt.datatransfer.Transferable;

+import javax.swing.*;
+

+import com.tigervnc.rfb.*;
+import com.tigervnc.rfb.Cursor;

+import com.tigervnc.rfb.Exception;
+import com.tigervnc.rfb.Point;
+

+class DesktopWindow extends JPanel implements

+                                   Runnable,

+                                   MouseListener,

+                                   MouseMotionListener,

+                                   MouseWheelListener,

+                                   KeyListener

+{

+

+  ////////////////////////////////////////////////////////////////////

+  // The following methods are all called from the RFB thread

+

+  public DesktopWindow(int width, int height, PixelFormat serverPF, CConn cc_) {

+    cc = cc_;

+    setSize(width, height);

+    im = new PixelBufferImage(width, height, cc, this);

+

+    cursor = new Cursor();

+    cursorBacking = new ManagedPixelBuffer();

+    addMouseListener(this);

+    addMouseWheelListener(this);

+    addMouseMotionListener(this);

+    addKeyListener(this);

+    addFocusListener(new FocusAdapter() {

+      public void focusGained(FocusEvent e) {

+        checkClipboard();

+      }

+    });

+    setFocusTraversalKeysEnabled(false);

+    setFocusable(true);

+    setDoubleBuffered(true);

+  }

+  

+  public int width() {

+    return getWidth();

+  }

+

+  public int height() {

+    return getHeight();

+  }

+

+  // initGraphics() is needed because for some reason you can't call

+  // getGraphics() on a newly-created awt Component.  It is called when the

+  // DesktopWindow has actually been made visible so that getGraphics() ought

+  // to work.

+

+  public void initGraphics() { 

+    cc.viewport.g = cc.viewport.getGraphics(); 

+    graphics = getComponentGraphics(cc.viewport.g);

+    prepareImage(im.image, -1, -1, this);

+  }

+

+  final public PixelFormat getPF() { return im.getPF(); }

+

+  synchronized public void setPF(PixelFormat pf) { 

+    im.setPF(pf); 

+  }

+

+  // Methods called from the RFB thread - these need to be synchronized

+  // wherever they access data shared with the GUI thread.

+

+  public void setCursor(int w, int h, Point hotspot,

+                                     int[] data, byte[] mask) {

+    // strictly we should use a mutex around this test since useLocalCursor

+    // might be being altered by the GUI thread.  However it's only a single

+    // boolean and it doesn't matter if we get the wrong value anyway.

+

+    synchronized(this) {

+    if (!cc.viewer.useLocalCursor.getValue()) return;

+

+    hideLocalCursor();

+

+    cursor.hotspot = hotspot;

+

+    Dimension bsc = tk.getBestCursorSize(w, h);

+

+    cursor.setSize(((int)bsc.getWidth() > w ? (int)bsc.getWidth() : w),

+                   ((int)bsc.getHeight() > h ? (int)bsc.getHeight() : h));

+    cursor.setPF(getPF());

+

+    cursorBacking.setSize(cursor.width(), cursor.height());

+    cursorBacking.setPF(getPF());

+

+    cursor.data = new int[cursor.width() * cursor.height()];

+    cursor.mask = new byte[cursor.maskLen()];

+

+    // set the masked pixels of the cursor transparent by using an extra bit in

+    // the colormap.  We'll OR this into the data based on the values in the mask.

+    if (cursor.getPF().bpp == 8) {

+      cursor.cm = new DirectColorModel(9, 7, (7 << 3), (3 << 6), (1 << 8));

+    }

+

+    int maskBytesPerRow = (w + 7) / 8;

+    for (int y = 0; y < h; y++) {

+      for (int x = 0; x < w; x++) {

+        int byte_ = y * maskBytesPerRow + x / 8;

+        int bit = 7 - x % 8;

+        if ((mask[byte_] & (1 << bit)) > 0) {

+          cursor.data[y * cursor.width() + x] = (cursor.getPF().bpp == 8) ?

+            data[y * w + x] | (1 << 8) : data[y * w + x];

+        }

+      }

+      System.arraycopy(mask, y * maskBytesPerRow, cursor.mask, 

+        y * ((cursor.width() + 7) / 8), maskBytesPerRow);

+    }

+

+    MemoryImageSource bitmap = 

+      new MemoryImageSource(cursor.width(), cursor.height(), cursor.cm,

+                            cursor.data, 0, cursor.width());

+    softCursor = 

+      tk.createCustomCursor(tk.createImage(bitmap), new java.awt.Point(hotspot.x,hotspot.y), "Cursor");

+    }

+

+    if (softCursor != null) {

+      setCursor(softCursor); 

+      cursorAvailable = true;

+      return;

+    }

+

+    if (!cursorAvailable) {

+      cursorAvailable = true;

+    }

+

+    showLocalCursor();

+    return;

+  }

+

+  // setColourMapEntries() changes some of the entries in the colourmap.

+  // Unfortunately these messages are often sent one at a time, so we delay the

+  // settings taking effect unless the whole colourmap has changed.  This is

+  // because getting java to recalculate its internal translation table and

+  // redraw the screen is expensive.

+

+  synchronized public void setColourMapEntries(int firstColour, int nColours,

+                                               int[] rgbs) {

+    im.setColourMapEntries(firstColour, nColours, rgbs);

+    if (nColours <= 256) {

+      im.updateColourMap();

+      im.put(0, 0, im.width(), im.height(), graphics);

+    } else {

+      if (setColourMapEntriesTimerThread == null) {

+        setColourMapEntriesTimerThread = new Thread(this);

+        setColourMapEntriesTimerThread.start();

+      }

+    }

+  }

+

+// Update the actual window with the changed parts of the framebuffer.

+

+  public void framebufferUpdateEnd()

+  {

+    drawInvalidRect();

+  }

+

+  // resize() is called when the desktop has changed size

+  synchronized public void resize() {

+    vlog.debug("DesktopWindow.resize() called");

+    int w = cc.cp.width;

+    int h = cc.cp.height;

+    hideLocalCursor();

+    setSize(w, h);

+    im.resize(w, h);

+  }

+

+  final void drawInvalidRect() {

+    if (!invalidRect) return;

+    int x = invalidLeft;

+    int w = invalidRight - x;

+    int y = invalidTop;

+    int h = invalidBottom - y;

+    invalidRect = false;

+

+    synchronized (this) {

+      im.put(x, y, w, h, graphics);

+    }

+  }

+

+  final void invalidate(int x, int y, int w, int h) {

+    if (invalidRect) {

+      if (x < invalidLeft) invalidLeft = x;

+      if (x + w > invalidRight) invalidRight = x + w;

+      if (y < invalidTop) invalidTop = y;

+      if (y + h > invalidBottom) invalidBottom = y + h;

+    } else {

+      invalidLeft = x;

+      invalidRight = x + w;

+      invalidTop = y;

+      invalidBottom = y + h;

+      invalidRect = true;

+    }

+

+    if ((invalidRight - invalidLeft) * (invalidBottom - invalidTop) > 100000)

+      drawInvalidRect();

+  }

+

+  public void beginRect(int x, int y, int w, int h, int encoding) {

+    invalidRect = false;

+  }

+

+  public void endRect(int x, int y, int w, int h, int encoding) {

+    drawInvalidRect();

+  }

+

+  synchronized final public void fillRect(int x, int y, int w, int h, int pix)

+  {

+    if (overlapsCursor(x, y, w, h)) hideLocalCursor();

+    im.fillRect(x, y, w, h, pix);

+    invalidate(x, y, w, h);

+    if (softCursor == null)

+      showLocalCursor();

+  }

+

+  synchronized final public void imageRect(int x, int y, int w, int h,

+                                           int[] pix) {

+    if (overlapsCursor(x, y, w, h)) hideLocalCursor();

+    im.imageRect(x, y, w, h, pix);

+    invalidate(x, y, w, h);

+    if (softCursor == null)

+      showLocalCursor();

+  }

+

+  synchronized final public void copyRect(int x, int y, int w, int h,

+                                          int srcX, int srcY) {

+    if (overlapsCursor(x, y, w, h) || overlapsCursor(srcX, srcY, w, h))

+      hideLocalCursor();

+    im.copyRect(x, y, w, h, srcX, srcY);

+    if (!cc.viewer.fastCopyRect.getValue()) {

+      invalidate(x, y, w, h);

+    }

+  }

+

+

+  // mutex MUST be held when overlapsCursor() is called

+  final boolean overlapsCursor(int x, int y, int w, int h) {

+    return (x < cursorBackingX + cursorBacking.width() &&

+            y < cursorBackingY + cursorBacking.height() &&

+            x+w > cursorBackingX && y+h > cursorBackingY);

+  }

+

+

+  ////////////////////////////////////////////////////////////////////

+  // The following methods are all called from the GUI thread

+

+  synchronized void resetLocalCursor() {

+    hideLocalCursor();

+    cursorAvailable = false;

+  }

+

+  synchronized public Dimension getPreferredSize() {

+    return new Dimension(im.width(), im.height());

+  }

+

+  synchronized public Dimension getMinimumSize() {

+    return new Dimension(im.width(), im.height());

+  }

+

+  public void update(Graphics g) {

+    //repaint();

+  }

+

+  synchronized public void paintComponent(Graphics g) {

+    Graphics2D g2 = (Graphics2D) g;

+    g2.drawImage(im.image, 0, 0, this);

+  }

+

+

+  String oldContents = "";

+  

+  synchronized public void checkClipboard() {

+    if (ClipboardDialog.systemClipboard != null &&

+        cc.viewer.sendClipboard.getValue()) {

+      Transferable t = ClipboardDialog.systemClipboard.getContents(this);

+      if ((t != null) && t.isDataFlavorSupported(DataFlavor.stringFlavor)) {

+        try {

+          String newContents = (String) t.getTransferData(DataFlavor.stringFlavor);

+          if (newContents != null && !newContents.equals(oldContents)) {

+            cc.writeClientCutText(newContents, newContents.length());

+            oldContents = newContents;

+            cc.clipboardDialog.setContents(newContents);

+          }

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

+          System.out.println("Exception getting clipboard data: " + e.getMessage());

+        }

+      }

+    }

+  }

+

+  /** Mouse-Motion callback function */

+  private void mouseMotionCB(MouseEvent e) {

+    if (!cc.viewer.viewOnly.getValue())

+      cc.writePointerEvent(e);

+    // - If local cursor rendering is enabled then use it

+    synchronized(this) {

+      if (cursorAvailable) {

+        // - Render the cursor!

+        if (e.getX() != cursorPosX || e.getY() != cursorPosY) {

+          hideLocalCursor();

+          if (e.getX() >= 0 && e.getX() < im.width() &&

+              e.getY() >= 0 && e.getY() < im.height()) {

+            cursorPosX = e.getX();

+            cursorPosY = e.getY();

+            if (softCursor == null)

+              showLocalCursor();

+          }

+        }

+      }

+    }

+    lastX = e.getX();

+    lastY = e.getY();      

+  }

+  public void mouseDragged(MouseEvent e) { mouseMotionCB(e);}

+  public void mouseMoved(MouseEvent e) { mouseMotionCB(e);}

+

+  /** Mouse callback function */

+  private void mouseCB(MouseEvent e) {

+    if (!cc.viewer.viewOnly.getValue())

+      cc.writePointerEvent(e);

+    lastX = e.getX();

+    lastY = e.getY();

+  }

+  public void mouseReleased(MouseEvent e){ mouseCB(e);}

+  public void mousePressed(MouseEvent e) { mouseCB(e);}

+  public void mouseClicked(MouseEvent e){}

+  public void mouseEntered(MouseEvent e){}

+  public void mouseExited(MouseEvent e){}  

+  

+  /** MouseWheel callback function */

+  private void mouseWheelCB(MouseWheelEvent e) {

+    if (!cc.viewer.viewOnly.getValue())

+      cc.writeWheelEvent(e);

+  }

+  public void mouseWheelMoved(MouseWheelEvent e){ 

+    mouseWheelCB(e);

+  }

+

+  /** Handle the key-typed event. */

+  public void keyTyped(KeyEvent e) {}

+  /** Handle the key-released event. */

+  public void keyReleased(KeyEvent e) {}

+  /** Handle the key-pressed event. */

+  public void keyPressed(KeyEvent e) {

+    if (e.getKeyCode() == 

+        (KeyEvent.VK_F1+cc.menuKey-Keysyms.F1)) {

+      cc.showMenu(lastX, lastY);

+      return;

+    }

+    if (!cc.viewer.viewOnly.getValue())

+      cc.writeKeyEvent(e);

+  }

+

+  ////////////////////////////////////////////////////////////////////

+  // The following methods are called from both RFB and GUI threads

+

+  // Note that mutex MUST be held when hideLocalCursor() and showLocalCursor()

+  // are called.

+

+  private void hideLocalCursor() {

+    // - Blit the cursor backing store over the cursor

+    if (cursorVisible) {

+      cursorVisible = false;

+      im.imageRect(cursorBackingX, cursorBackingY, cursorBacking.width(),

+                   cursorBacking.height(), cursorBacking.data);

+      im.put(cursorBackingX, cursorBackingY, cursorBacking.width(),

+             cursorBacking.height(), graphics);

+    }

+  }

+

+  private void showLocalCursor() {

+    if (cursorAvailable && !cursorVisible) {

+      if (!im.getPF().equal(cursor.getPF()) ||

+          cursor.width() == 0 || cursor.height() == 0) {

+        vlog.debug("attempting to render invalid local cursor");

+        cursorAvailable = false;

+        return;

+      }

+      cursorVisible = true;

+      if (softCursor != null) return;

+

+      int cursorLeft = (int)cursor.hotspot.x;

+      int cursorTop = (int)cursor.hotspot.y;

+      int cursorRight = cursorLeft + cursor.width();

+      int cursorBottom = cursorTop + cursor.height();

+

+      int x = (cursorLeft >= 0 ? cursorLeft : 0);

+      int y = (cursorTop >= 0 ? cursorTop : 0);

+      int w = ((cursorRight < im.width() ? cursorRight : im.width()) - x);

+      int h = ((cursorBottom < im.height() ? cursorBottom : im.height()) - y);

+

+      cursorBackingX = x;

+      cursorBackingY = y;

+      cursorBacking.setSize(w, h);

+      

+      for (int j = 0; j < h; j++)

+        System.arraycopy(im.data, (y+j) * im.width() + x,

+                         cursorBacking.data, j*w, w);

+

+      im.maskRect(cursorLeft, cursorTop, cursor.width(), cursor.height(),

+                  cursor.data, cursor.mask);

+      im.put(x, y, w, h, graphics);

+    }

+  }

+

+

+  // run() is executed by the setColourMapEntriesTimerThread - it sleeps for

+  // 100ms before actually updating the colourmap.

+  public void run() {

+    try {

+      Thread.sleep(100);

+    } catch (InterruptedException e) {}

+    synchronized (this) {

+      im.updateColourMap();

+      im.put(0, 0, im.width(), im.height(), graphics);

+      setColourMapEntriesTimerThread = null;

+    }

+  }

+

+  // access to cc by different threads is specified in CConn

+  CConn cc;

+

+  // access to the following must be synchronized:

+  PixelBufferImage im;

+  Graphics graphics;

+  Thread setColourMapEntriesTimerThread;

+

+  Cursor cursor;

+  boolean cursorVisible;     // Is cursor currently rendered?

+  boolean cursorAvailable;   // Is cursor available for rendering?

+  int cursorPosX, cursorPosY;

+  ManagedPixelBuffer cursorBacking;

+  int cursorBackingX, cursorBackingY;

+  java.awt.Cursor softCursor;

+  static Toolkit tk = Toolkit.getDefaultToolkit();

+

+  // the following are only ever accessed by the RFB thread:

+  boolean invalidRect;

+  int invalidLeft, invalidRight, invalidTop, invalidBottom;

+

+  // the following are only ever accessed by the GUI thread:

+  int lastX, lastY;

+

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

+}

diff --git a/java/src/com/tigervnc/vncviewer/Dialog.java b/java/src/com/tigervnc/vncviewer/Dialog.java
index 9572b9f..6a11fb3 100644
--- a/java/src/com/tigervnc/vncviewer/Dialog.java
+++ b/java/src/com/tigervnc/vncviewer/Dialog.java
@@ -1,5 +1,4 @@
 /* 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
@@ -16,6 +15,7 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
  * USA.
  */
+
 //
 // This Dialog class implements a pop-up dialog.  This is needed because
 // apparently you can't use the standard AWT Dialog from within an applet.  The
@@ -27,11 +27,24 @@
 
 package com.tigervnc.vncviewer;
 
+import java.io.*;
+import java.net.*;
 import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.*;
+import javax.swing.*;
+import javax.swing.filechooser.*;
 
-class Dialog extends Frame {
+//class Dialog extends JFrame implements WindowListener {
+class Dialog extends JFrame {
 
-  public Dialog(boolean modal_) { modal = modal_; }
+  protected boolean ok, done;
+  boolean modal;
+
+  public Dialog(boolean modal_) {
+    modal = modal_;
+    //addWindowListener(this);
+  }
 
   public boolean showDialog() {
     ok = false;
@@ -42,7 +55,14 @@
     int x = (dpySize.width - mySize.width) / 2;
     int y = (dpySize.height - mySize.height) / 2;
     setLocation(x, y);
-    show();
+    ClassLoader cl = this.getClass().getClassLoader();
+    ImageIcon icon = new ImageIcon(cl.getResource("com/tigervnc/vncviewer/tigervnc.ico"));
+    setIconImage(icon.getImage());
+    //setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+    //setFont(new Font("SansSerif", Font.PLAIN, 11));
+
+    setVisible(true);
+    setFocusable(true);
     if (!modal) return true;
     synchronized(this) {
       try {
@@ -56,7 +76,8 @@
 
   public void endDialog() {
     done = true;
-    hide();
+    setVisible(false);
+    setFocusable(false);
     if (modal) {
       synchronized (this) {
         notify();
@@ -66,16 +87,57 @@
 
   // initDialog() can be overridden in a derived class.  Typically it is used
   // to make sure that checkboxes have the right state, etc.
-  public void initDialog() {}
-
-  public boolean handleEvent(Event event) {
-    if (event.id == Event.WINDOW_DESTROY) {
-      ok = false;
-      endDialog();
-    }   
-    return super.handleEvent(event);
+  public void initDialog() {
   }
 
-  protected boolean ok, done;
-  boolean modal;
+  //------------------------------------------------------------------ 
+  //   implemented blank methods
+  //public void windowClosed(WindowEvent event){}
+  //public void windowDeiconified(WindowEvent event){}
+  //public void windowIconified(WindowEvent event){}
+  //public void windowActivated(WindowEvent event){}
+  //public void windowDeactivated(WindowEvent event){}
+  //public void windowOpened(WindowEvent event){}
+
+  //------------------------------------------------------------------
+
+  // method to check which window was closing
+  //public void windowClosing(WindowEvent event) {
+  //  ok = false;
+  //  endDialog();
+  //}
+
+  public void addGBComponent(JComponent c, JComponent cp,
+                             int gx, int gy, 
+                             int gw, int gh, 
+                             int gipx, int gipy,
+                             double gwx, double gwy, 
+                             int fill, int anchor,
+                             Insets insets)
+  {
+      GridBagConstraints gbc = new GridBagConstraints();
+      gbc.anchor = anchor;
+      gbc.fill = fill;
+      gbc.gridx = gx;
+      gbc.gridy = gy;
+      gbc.gridwidth = gw;
+      gbc.gridheight = gh;
+      gbc.insets = insets;
+      gbc.ipadx = gipx;
+      gbc.ipady = gipy;
+      gbc.weightx = gwx;
+      gbc.weighty = gwy;
+      cp.add(c, gbc);
+  }
+
+  final public String getFileSeperator() {
+    String seperator = System.getProperties().get("file.separator").toString();
+    return seperator;
+  }
+
+  final public String getUserName() {
+    String userName = (String)System.getProperties().get("user.name");
+    return userName;
+  }
+
 }
diff --git a/java/src/com/tigervnc/vncviewer/F8Menu.java b/java/src/com/tigervnc/vncviewer/F8Menu.java
new file mode 100644
index 0000000..5838b38
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/F8Menu.java
@@ -0,0 +1,133 @@
+/* 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.vncviewer;
+
+import java.awt.Cursor;
+import java.awt.event.*;
+import javax.swing.JFrame;
+import javax.swing.JPopupMenu;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JCheckBoxMenuItem;
+
+import com.tigervnc.rfb.*;
+
+public class F8Menu extends JPopupMenu implements ActionListener {
+  public F8Menu(CConn cc_) {
+    super("VNC Menu");
+    setLightWeightPopupEnabled(false);
+    cc = cc_;
+    restore    = addMenuItem("Restore",KeyEvent.VK_R);
+    move       = addMenuItem("Move");
+    move.setEnabled(false);
+    size       = addMenuItem("Size");
+    size.setEnabled(false);
+    minimize   = addMenuItem("Minimize", KeyEvent.VK_N);
+    maximize   = addMenuItem("Maximize", KeyEvent.VK_X);
+    addSeparator();
+    exit       = addMenuItem("Close Viewer", KeyEvent.VK_C);
+    addSeparator();
+    fullScreen = new JCheckBoxMenuItem("Full Screen");
+    fullScreen.setMnemonic(KeyEvent.VK_F);
+    fullScreen.addActionListener(this);
+    add(fullScreen);
+    addSeparator();
+    clipboard  = addMenuItem("Clipboard...");
+    addSeparator();
+    f8 = addMenuItem("Send F"+(cc.menuKey-Keysyms.F1+1));
+    ctrlAltDel = addMenuItem("Send Ctrl-Alt-Del");
+    addSeparator();
+    refresh    = addMenuItem("Refresh Screen", KeyEvent.VK_H);
+    addSeparator();
+    newConn    = addMenuItem("New connection...", KeyEvent.VK_W);
+    options    = addMenuItem("Options...", KeyEvent.VK_O);
+    info       = addMenuItem("Connection info...", KeyEvent.VK_I);
+    about      = addMenuItem("About VNCviewer...", KeyEvent.VK_A);
+    addSeparator();
+    dismiss    = addMenuItem("Dismiss menu");
+    setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+  }
+
+  JMenuItem addMenuItem(String str, int mnemonic) {
+    JMenuItem item = new JMenuItem(str, mnemonic);
+    item.addActionListener(this);
+    add(item);
+    return item;
+  }
+
+  JMenuItem addMenuItem(String str) {
+    JMenuItem item = new JMenuItem(str);
+    item.addActionListener(this);
+    add(item);
+    return item;
+  }
+
+  boolean actionMatch(ActionEvent ev, JMenuItem item) {
+    return ev.getActionCommand().equals(item.getActionCommand());
+  }
+
+  public void actionPerformed(ActionEvent ev) {
+    if (actionMatch(ev, exit)) {
+      cc.close();
+    } else if (actionMatch(ev, fullScreen)) {
+      cc.toggleFullScreen();
+    } else if (actionMatch(ev, restore)) {
+      if (cc.fullScreen) cc.toggleFullScreen();
+      cc.viewport.setExtendedState(JFrame.NORMAL);
+    } else if (actionMatch(ev, minimize)) {
+      if (cc.fullScreen) cc.toggleFullScreen();
+      cc.viewport.setExtendedState(JFrame.ICONIFIED);
+    } else if (actionMatch(ev, maximize)) {
+      if (cc.fullScreen) cc.toggleFullScreen();
+      cc.viewport.setExtendedState(JFrame.MAXIMIZED_BOTH);
+    } else if (actionMatch(ev, clipboard)) {
+      cc.clipboardDialog.showDialog();
+    } else if (actionMatch(ev, f8)) {
+      cc.writeKeyEvent(cc.menuKey, true);
+      cc.writeKeyEvent(cc.menuKey, false);
+    } else if (actionMatch(ev, ctrlAltDel)) {
+      cc.writeKeyEvent(Keysyms.Control_L, true);
+      cc.writeKeyEvent(Keysyms.Alt_L, true);
+      cc.writeKeyEvent(Keysyms.Delete, true);
+      cc.writeKeyEvent(Keysyms.Delete, false);
+      cc.writeKeyEvent(Keysyms.Alt_L, false);
+      cc.writeKeyEvent(Keysyms.Control_L, false);
+    } else if (actionMatch(ev, refresh)) {
+      cc.refresh();
+    } else if (actionMatch(ev, newConn)) {
+      VncViewer.newViewer(cc.viewer);
+    } else if (actionMatch(ev, options)) {
+      cc.options.showDialog();
+    } else if (actionMatch(ev, info)) {
+      cc.showInfo();
+    } else if (actionMatch(ev, about)) {
+      cc.showAbout();
+    } else if (actionMatch(ev, dismiss)) {
+      firePopupMenuCanceled();
+    }
+  }
+
+  CConn cc;
+  JMenuItem restore, move, size, minimize, maximize;
+  JMenuItem exit, clipboard, ctrlAltDel, refresh;
+  JMenuItem newConn, options, info, about, dismiss;
+  static JMenuItem f8;
+  JCheckBoxMenuItem fullScreen;
+  static LogWriter vlog = new LogWriter("F8Menu");
+}
diff --git a/java/src/com/tigervnc/vncviewer/HTTPConnectSocket.java b/java/src/com/tigervnc/vncviewer/HTTPConnectSocket.java
deleted file mode 100644
index 2a66b25..0000000
--- a/java/src/com/tigervnc/vncviewer/HTTPConnectSocket.java
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-//  Copyright (C) 2002 Constantin Kaplinsky, Inc.  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.
-//
-
-//
-// HTTPConnectSocket.java together with HTTPConnectSocketFactory.java
-// implement an alternate way to connect to VNC servers via one or two
-// HTTP proxies supporting the HTTP CONNECT method.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.net.*;
-import java.io.*;
-
-class HTTPConnectSocket extends Socket {
-
-  public HTTPConnectSocket(String host, int port,
-			   String proxyHost, int proxyPort)
-    throws IOException {
-
-    // Connect to the specified HTTP proxy
-    super(proxyHost, proxyPort);
-
-    // Send the CONNECT request
-    getOutputStream().write(("CONNECT " + host + ":" + port +
-			     " HTTP/1.0\r\n\r\n").getBytes());
-
-    // Read the first line of the response
-    DataInputStream is = new DataInputStream(getInputStream());
-    String str = is.readLine();
-
-    // Check the HTTP error code -- it should be "200" on success
-    if (!str.startsWith("HTTP/1.0 200 ")) {
-      if (str.startsWith("HTTP/1.0 "))
-	str = str.substring(9);
-      throw new IOException("Proxy reports \"" + str + "\"");
-    }
-
-    // Success -- skip remaining HTTP headers
-    do {
-      str = is.readLine();
-    } while (str.length() != 0);
-  }
-}
-
diff --git a/java/src/com/tigervnc/vncviewer/HTTPConnectSocketFactory.java b/java/src/com/tigervnc/vncviewer/HTTPConnectSocketFactory.java
deleted file mode 100644
index 56c8b97..0000000
--- a/java/src/com/tigervnc/vncviewer/HTTPConnectSocketFactory.java
+++ /dev/null
@@ -1,88 +0,0 @@
-//
-//  Copyright (C) 2002 Constantin Kaplinsky, Inc.  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.
-//
-
-//
-// HTTPConnectSocketFactory.java together with HTTPConnectSocket.java
-// implement an alternate way to connect to VNC servers via one or two
-// HTTP proxies supporting the HTTP CONNECT method.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.applet.*;
-import java.net.*;
-import java.io.*;
-
-class HTTPConnectSocketFactory implements SocketFactory {
-
-  public Socket createSocket(String host, int port, Applet applet)
-    throws IOException {
-
-    return createSocket(host, port,
-			applet.getParameter("PROXYHOST1"),
-			applet.getParameter("PROXYPORT1"));
-  }
-
-  public Socket createSocket(String host, int port, String[] args)
-    throws IOException {
-
-    return createSocket(host, port,
-			readArg(args, "PROXYHOST1"),
-			readArg(args, "PROXYPORT1"));
-  }
-
-  public Socket createSocket(String host, int port,
-			     String proxyHost, String proxyPortStr)
-    throws IOException {
-
-    int proxyPort = 0;
-    if (proxyPortStr != null) {
-      try {
-	proxyPort = Integer.parseInt(proxyPortStr);
-      } catch (NumberFormatException e) { }
-    }
-
-    if (proxyHost == null || proxyPort == 0) {
-      System.out.println("Incomplete parameter list for HTTPConnectSocket");
-      return new Socket(host, port);
-    }
-
-    System.out.println("HTTP CONNECT via proxy " + proxyHost +
-		       " port " + proxyPort);
-    HTTPConnectSocket s =
-      new HTTPConnectSocket(host, port, proxyHost, proxyPort);
-
-    return (Socket)s;
-  }
-
-  private String readArg(String[] args, String name) {
-
-    for (int i = 0; i < args.length; i += 2) {
-      if (args[i].equalsIgnoreCase(name)) {
-	try {
-	  return args[i+1];
-	} catch (Exception e) {
-	  return null;
-	}
-      }
-    }
-    return null;
-  }
-}
-
diff --git a/java/src/com/tigervnc/vncviewer/InStream.java b/java/src/com/tigervnc/vncviewer/InStream.java
deleted file mode 100644
index 75ff91a..0000000
--- a/java/src/com/tigervnc/vncviewer/InStream.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/* 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.
- */
-
-//
-// rdr::InStream marshalls data from a buffer stored in RDR (RFB Data
-// Representation).
-//
-
-package com.tigervnc.vncviewer;
-
-abstract public class InStream {
-
-  // check() ensures there is buffer data for at least one item of size
-  // itemSize bytes.  Returns the number of items in the buffer (up to a
-  // maximum of nItems).
-
-  public final int check(int itemSize, int nItems) throws Exception {
-    if (ptr + itemSize * nItems > end) {
-      if (ptr + itemSize > end)
-        return overrun(itemSize, nItems);
-
-      nItems = (end - ptr) / itemSize;
-    }
-    return nItems;
-  }
-
-  public final void check(int itemSize) throws Exception {
-    if (ptr + itemSize > end)
-      overrun(itemSize, 1);
-  }
-
-  // readU/SN() methods read unsigned and signed N-bit integers.
-
-  public final int readS8() throws Exception {
-    check(1); return b[ptr++];
-  }
-
-  public final int readS16() throws Exception {
-    check(2); int b0 = b[ptr++];
-    int b1 = b[ptr++] & 0xff; return b0 << 8 | b1;
-  }
-
-  public final int readS32() throws Exception {
-    check(4); int b0 = b[ptr++];
-    int b1 = b[ptr++] & 0xff;
-    int b2 = b[ptr++] & 0xff;
-    int b3 = b[ptr++] & 0xff;
-    return b0 << 24 | b1 << 16 | b2 << 8 | b3;
-  }
-
-  public final int readU8() throws Exception {
-    return readS8() & 0xff;
-  }
-
-  public final int readU16() throws Exception {
-    return readS16() & 0xffff;
-  }
-
-  public final int readU32() throws Exception {
-    return readS32() & 0xffffffff;
-  }
-
-  // readString() reads a string - a U32 length followed by the data.
-
-  public final String readString() throws Exception {
-    int len = readU32();
-    if (len > maxStringLength)
-      throw new Exception("InStream max string length exceeded");
-
-    char[] str = new char[len];
-    int i = 0;
-    while (i < len) {
-      int j = i + check(1, len - i);
-      while (i < j) {
-	str[i++] = (char)b[ptr++];
-      }
-    }
-
-    return new String(str);
-  }
-
-  // maxStringLength protects against allocating a huge buffer.  Set it
-  // higher if you need longer strings.
-
-  public static int maxStringLength = 65535;
-
-  public final void skip(int bytes) throws Exception {
-    while (bytes > 0) {
-      int n = check(1, bytes);
-      ptr += n;
-      bytes -= n;
-    }
-  }
-
-  // readBytes() reads an exact number of bytes into an array at an offset.
-
-  public void readBytes(byte[] data, int offset, int length) throws Exception {
-    int offsetEnd = offset + length;
-    while (offset < offsetEnd) {
-      int n = check(1, offsetEnd - offset);
-      System.arraycopy(b, ptr, data, offset, n);
-      ptr += n;
-      offset += n;
-    }
-  }
-
-  // readOpaqueN() reads a quantity "without byte-swapping".  Because java has
-  // no byte-ordering, we just use big-endian.
-
-  public final int readOpaque8() throws Exception {
-    return readU8();
-  }
-
-  public final int readOpaque16() throws Exception {
-    return readU16();
-  }
-
-  public final int readOpaque32() throws Exception {
-    return readU32();
-  }
-
-  public final int readOpaque24A() throws Exception {
-    check(3); int b0 = b[ptr++];
-    int b1 = b[ptr++]; int b2 = b[ptr++];
-    return b0 << 24 | b1 << 16 | b2 << 8;
-  }
-
-  public final int readOpaque24B() throws Exception {
-    check(3); int b0 = b[ptr++];
-    int b1 = b[ptr++]; int b2 = b[ptr++];
-    return b0 << 16 | b1 << 8 | b2;
-  }
-
-  // pos() returns the position in the stream.
-
-  abstract public int pos();
-
-  // bytesAvailable() returns true if at least one byte can be read from the
-  // stream without blocking.  i.e. if false is returned then readU8() would
-  // block.
-
-  public boolean bytesAvailable() { return end != ptr; }
-
-  // getbuf(), getptr(), getend() and setptr() are "dirty" methods which allow
-  // you to manipulate the buffer directly.  This is useful for a stream which
-  // is a wrapper around an underlying stream.
-
-  public final byte[] getbuf() { return b; }
-  public final int getptr() { return ptr; }
-  public final int getend() { return end; }
-  public final void setptr(int p) { ptr = p; }
-
-  // overrun() is implemented by a derived class to cope with buffer overrun.
-  // It ensures there are at least itemSize bytes of buffer data.  Returns
-  // the number of items in the buffer (up to a maximum of nItems).  itemSize
-  // is supposed to be "small" (a few bytes).
-
-  abstract protected int overrun(int itemSize, int nItems) throws Exception;
-
-  protected InStream() {}
-  protected byte[] b;
-  protected int ptr;
-  protected int end;
-}
diff --git a/java/src/com/tigervnc/vncviewer/Makefile b/java/src/com/tigervnc/vncviewer/Makefile
index 39ccbb7..99c04ac 100644
--- a/java/src/com/tigervnc/vncviewer/Makefile
+++ b/java/src/com/tigervnc/vncviewer/Makefile
@@ -11,25 +11,15 @@
 PAGES = index.vnc
 INSTALL_DIR = /usr/local/vnc/classes
 
-CLASSES = VncViewer.class RfbProto.class AuthPanel.class VncCanvas.class \
-	  VncCanvas2.class \
-	  OptionsFrame.class ClipboardFrame.class ButtonPanel.class \
-	  DesCipher.class \
-	  RecordingFrame.class SessionRecorder.class \
-	  SocketFactory.class HTTPConnectSocketFactory.class \
-	  HTTPConnectSocket.class ReloginPanel.class \
-	  InStream.class MemInStream.class ZlibInStream.class \
-	  TLSTunnelBase.class TLSTunnel.class X509Tunnel.class Dialog.class MessageBox.class
+CLASSES = CConn.class OptionsDialogCallback.class ClipboardDialog.class \
+    PasswdDialog.class DesktopWindow.class PixelBufferImage.class \
+    Dialog.class ServerDialog.class F8Menu.class	UserPrefs.class \
+    OptionsDialog.class VncViewer.class
 
-SOURCES = VncViewer.java RfbProto.java AuthPanel.java VncCanvas.java \
-	  VncCanvas2.java \
-	  OptionsFrame.java ClipboardFrame.java ButtonPanel.java \
-	  DesCipher.java \
-	  RecordingFrame.java SessionRecorder.java \
-	  SocketFactory.java HTTPConnectSocketFactory.java \
-	  HTTPConnectSocket.java ReloginPanel.java \
-	  InStream.java MemInStream.java ZlibInStream.java \
-	  TLSTunnelBase.java TLSTunnel.java X509Tunnel.java Dialog.java MessageBox.java
+SOURCES = CConn.java OptionsDialogCallback.java ClipboardDialog.java \
+    PasswdDialog.java DesktopWindow.java PixelBufferImage.java \
+    Dialog.java ServerDialog.java F8Menu.java	UserPrefs.java \
+    OptionsDialog.java VncViewer.java
 
 all: $(CLASSES) $(ARCHIVE)
 
@@ -41,8 +31,10 @@
 	$(JAR) cfm com/tigervnc/vncviewer/$(ARCHIVE) \
 		com/tigervnc/vncviewer/$(MANIFEST) \
 		com/tigervnc/vncviewer/*.class \
-		com/tigervnc/decoder/*.class \
-		com/tigervnc/decoder/common/*.class
+		com/tigervnc/rfb/*.class \
+		com/tigervnc/rdr/*.class \
+		com/tigervnc/vncviewer/tigervnc.png \
+		com/tigervnc/vncviewer/tigervnc.ico
 
 install: $(CLASSES) $(ARCHIVE)
 	$(CP) $(CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR)
@@ -51,4 +43,4 @@
 	@$(ExportJavaClasses)
 
 clean::
-	$(RM) *.class *.jar ../decoder/*.class ../decoder/common/*.class
+	$(RM) *.class *.jar ../rfb/*.class ../rdr/*.class
diff --git a/java/src/com/tigervnc/vncviewer/MessageBox.java b/java/src/com/tigervnc/vncviewer/MessageBox.java
deleted file mode 100644
index feac5ce..0000000
--- a/java/src/com/tigervnc/vncviewer/MessageBox.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/* 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.vncviewer;
-
-import java.awt.*;
-
-public class MessageBox extends com.tigervnc.vncviewer.Dialog {
-
-  public static final int MB_OK = 0;
-  public static final int MB_OKAYCANCEL = 1;
-  public static final int MB_YESNO = 2;
-
-  public MessageBox(String msg, int flags) {
-    super(true);
-    GridLayout g = new GridLayout(0,1);
-    setLayout(g);
-    while (true) {
-      int i = msg.indexOf('\n');
-      int j = (i==-1) ? msg.length() : i;
-      add(new Label(msg.substring(0, j)));
-      if (i==-1) break;
-      msg = msg.substring(j+1);
-    }
-    Panel p2 = new Panel();
-    switch (flags & 3) {
-    case MB_OKAYCANCEL:
-      cancelButton = new Button("Cancel");
-      // No break
-    case MB_OK:
-      okButton = new Button("OK");
-      break;
-    case MB_YESNO:
-      okButton = new Button("Yes");
-      cancelButton = new Button("No");
-      break;
-    }
-    if (okButton != null) p2.add(okButton);
-    if (cancelButton != null) p2.add(cancelButton);
-    add("South", p2);
-    pack();
-    showDialog();
-  }
-
-  public MessageBox(String msg) {
-    this(msg, MB_OK);
-  }
-
-
-  public boolean action(Event event, Object arg) {
-    if (event.target == okButton) {
-      ok = true;
-      endDialog();
-    } else if (event.target == cancelButton) {
-      ok = false;
-      endDialog();
-    }
-    return true;
-  }
-
-  Button okButton, cancelButton;
-
-  public boolean result() {
-    return ok;
-  }
-
-}
diff --git a/java/src/com/tigervnc/vncviewer/OptionsDialog.java b/java/src/com/tigervnc/vncviewer/OptionsDialog.java
new file mode 100644
index 0000000..7e1828e
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/OptionsDialog.java
@@ -0,0 +1,380 @@
+/* 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.vncviewer;
+

+import java.awt.*;

+import java.awt.event.*;

+import javax.swing.*;

+import javax.swing.border.*;

+import javax.swing.filechooser.*;

+import javax.swing.ImageIcon;

+import java.net.URL;

+import java.io.IOException;

+
+import com.tigervnc.rfb.*;
+import com.tigervnc.rfb.Exception;
+

+class OptionsDialog extends Dialog implements

+                            ActionListener,

+                            ItemListener

+{

+

+  // Constants

+  // Static variables

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

+

+  OptionsDialogCallback cb;

+  JPanel FormatPanel, InputsPanel, MiscPanel, DefaultsPanel, SecPanel;

+  JCheckBox autoSelect, customCompressLevel, noJpeg;

+  JComboBox menuKey, compressLevel, qualityLevel ;

+  ButtonGroup encodingGroup, colourGroup;

+  JRadioButton zrle, hextile, tight, raw;

+  JRadioButton fullColour, mediumColour, lowColour, veryLowColour;

+  JCheckBox viewOnly, acceptClipboard, sendClipboard;

+  JCheckBox fullScreen, shared, useLocalCursor, fastCopyRect;

+  JCheckBox secVeNCrypt, encNone, encTLS, encX509;

+  JCheckBox secNone, secVnc, secPlain, secManaged, sendLocalUsername;

+  JButton okButton, cancelButton;

+  JButton ca, crl;

+  JButton defSaveButton;

+  boolean encryption = true; 

+  UserPrefs defaults;

+

+  public OptionsDialog(OptionsDialogCallback cb_) { 

+    super(false);

+    cb = cb_;

+    setResizable(false);

+    setTitle("VNC Viewer Options");

+    defaults = new UserPrefs("vncviewer");

+

+    getContentPane().setLayout(

+      new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS));

+	

+    JTabbedPane tabPane = new JTabbedPane();

+

+    ButtonGroup encodingGroup = new ButtonGroup();

+    ButtonGroup colourGroup = new ButtonGroup();

+

+    // Colour & Encoding tab

+    FormatPanel=new JPanel(new GridBagLayout());

+

+    autoSelect = new JCheckBox("Auto Select");

+    autoSelect.addItemListener(this);

+

+    JPanel encodingPanel = new JPanel(new GridBagLayout());

+    encodingPanel.setBorder(BorderFactory.createTitledBorder("Preferred encoding"));

+    zrle = addRadioCheckbox("ZRLE", encodingGroup, encodingPanel);

+    hextile = addRadioCheckbox("Hextile", encodingGroup, encodingPanel);

+    tight = addRadioCheckbox("Tight", encodingGroup, encodingPanel);

+    raw = addRadioCheckbox("Raw", encodingGroup, encodingPanel);

+

+    JPanel tightPanel = new JPanel(new GridBagLayout());

+    customCompressLevel = new JCheckBox("Custom Compression Level");

+    customCompressLevel.addItemListener(this);

+    String[] compressionLevels = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };

+    compressLevel  = new JComboBox(compressionLevels);

+    JLabel compressionLabel = new JLabel("Level (1=fast, 9=best)");

+    noJpeg = new JCheckBox("Allow JPEG Compression");

+    noJpeg.addItemListener(this);

+    String[] qualityLevels = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };

+    qualityLevel  = new JComboBox(qualityLevels);

+    JLabel qualityLabel = new JLabel("Level (1=poor, 9=best)");

+    addGBComponent(customCompressLevel, tightPanel, 0, 0, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,2,0,0));

+    addGBComponent(compressLevel, tightPanel,       0, 1, 1, 1, 2, 2, 0, 0, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(0,20,0,0));

+    addGBComponent(compressionLabel, tightPanel,    1, 1, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,5,0,0));

+    addGBComponent(noJpeg, tightPanel,              0, 2, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,2,0,0));

+    addGBComponent(qualityLevel, tightPanel,        0, 3, 1, 1, 2, 2, 0, 0, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(0,20,0,0));

+    addGBComponent(qualityLabel, tightPanel,        1, 3, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,5,0,0));

+

+

+    JPanel colourPanel = new JPanel(new GridBagLayout());

+    colourPanel.setBorder(BorderFactory.createTitledBorder("Colour level"));

+    fullColour = addRadioCheckbox("Full (all available colours)", colourGroup, colourPanel);

+    mediumColour = addRadioCheckbox("Medium (256 colours)", colourGroup, colourPanel);

+    lowColour = addRadioCheckbox("Low (64 colours)", colourGroup, colourPanel);

+    veryLowColour = addRadioCheckbox("Very low(8 colours)", colourGroup, colourPanel);

+

+    addGBComponent(autoSelect,FormatPanel,    0, 0, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,2,0,0));

+    addGBComponent(encodingPanel,FormatPanel, 0, 1, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,2,0,0));

+    addGBComponent(colourPanel,FormatPanel,   1, 1, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_END, new Insets(0,2,0,0));

+    addGBComponent(tightPanel,FormatPanel,    0, 2, 2, GridBagConstraints.REMAINDER, 2, 2, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,2,0,0));

+

+    // Inputs tab

+    InputsPanel=new JPanel(new GridBagLayout());

+

+    viewOnly = new JCheckBox("View Only (ignore mouse & keyboard)");

+    viewOnly.addItemListener(this);

+    acceptClipboard = new JCheckBox("Accept clipboard from server");

+    acceptClipboard.addItemListener(this);

+    sendClipboard = new JCheckBox("Send clipboard to server");

+    sendClipboard.addItemListener(this);

+    JLabel menuKeyLabel = new JLabel("Menu Key");

+    String[] menuKeys = 

+      { "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12" };

+    menuKey  = new JComboBox(menuKeys);

+    menuKey.addItemListener(this);

+    addGBComponent(viewOnly,InputsPanel,        0, 0, 2, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4));

+    addGBComponent(acceptClipboard,InputsPanel, 0, 1, 2, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4));

+    addGBComponent(sendClipboard,InputsPanel,   0, 2, 2, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4));

+    addGBComponent(menuKeyLabel,InputsPanel,    0, 3, 1, GridBagConstraints.REMAINDER, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(8,10,0,4));

+    addGBComponent(menuKey,InputsPanel,         1, 3, 1, GridBagConstraints.REMAINDER, 0, 0, 2, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(4,4,0,125));

+    //((javax.swing.plaf.basic.BasicComboBoxRenderer)menuKey.getRenderer()).setBorder(new EmptyBorder(0,3,0,3));

+

+    // Misc tab

+    MiscPanel=new JPanel(new GridBagLayout());

+

+    fullScreen = new JCheckBox("Full-screen mode");

+    fullScreen.addItemListener(this);

+    shared = new JCheckBox("Shared connection (do not disconnect other viewers)");

+    shared.addItemListener(this);

+    useLocalCursor = new JCheckBox("Render cursor locally");

+    useLocalCursor.addItemListener(this);

+    fastCopyRect = new JCheckBox("Fast CopyRect");

+    fastCopyRect.addItemListener(this);

+    addGBComponent(fullScreen,MiscPanel,     0, 0, 1, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4));

+    addGBComponent(shared,MiscPanel,         0, 1, 1, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4));

+    addGBComponent(useLocalCursor,MiscPanel, 0, 2, 1, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4));

+    addGBComponent(fastCopyRect,MiscPanel,   0, 3, 1, GridBagConstraints.REMAINDER, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(4,4,0,4));

+

+    // load/save tab

+    DefaultsPanel=new JPanel(new GridBagLayout());

+

+    JPanel configPanel = new JPanel(new GridBagLayout());

+    configPanel.setBorder(BorderFactory.createTitledBorder("Configuration File"));

+    JButton cfReloadButton = new JButton("Reload");

+    cfReloadButton.addActionListener(this);

+    addGBComponent(cfReloadButton,configPanel, 0, 0, 1, 1, 0, 0, 0, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8));

+    JButton cfSaveButton = new JButton("Save");

+    cfSaveButton.addActionListener(this);

+    addGBComponent(cfSaveButton,configPanel, 0, 1, 1, 1, 0, 0, 0, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8));

+    JButton cfSaveAsButton = new JButton("Save As...");

+    cfSaveAsButton.addActionListener(this);

+    addGBComponent(cfSaveAsButton,configPanel, 0, 2, 1, 1, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8));

+    cfReloadButton.setEnabled(false);

+    cfSaveButton.setEnabled(false);

+

+    JPanel defaultsPanel = new JPanel(new GridBagLayout());

+    defaultsPanel.setBorder(BorderFactory.createTitledBorder("Defaults"));

+    JButton defReloadButton = new JButton("Reload");

+    defReloadButton.addActionListener(this);

+    addGBComponent(defReloadButton,defaultsPanel, 0, 0, 1, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8));

+    defSaveButton = new JButton("Save");

+    defSaveButton.addActionListener(this);

+    addGBComponent(defSaveButton,defaultsPanel, 0, 1, 1, 1, 0, 0, 0, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8));

+

+    addGBComponent(configPanel,DefaultsPanel, 0, 0, 1, GridBagConstraints.REMAINDER, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.PAGE_START, new Insets(4,4,4,4));

+    addGBComponent(defaultsPanel,DefaultsPanel, 1, 0, 1, GridBagConstraints.REMAINDER, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.PAGE_START, new Insets(4,4,4,4));

+

+    // security tab

+    SecPanel=new JPanel(new GridBagLayout());

+

+    JPanel encryptionPanel = new JPanel(new GridBagLayout());

+    encryptionPanel.setBorder(BorderFactory.createTitledBorder("Session Encryption"));

+    encNone = addCheckbox("None", null, encryptionPanel);

+    encTLS = addCheckbox("Anonymous TLS", null, encryptionPanel);

+    encX509 = addJCheckBox("TLS with X.509 certificates", null, encryptionPanel, new GridBagConstraints(0,2,1,1,1,1,GridBagConstraints.LINE_START,GridBagConstraints.REMAINDER,new Insets(0,0,0,60),0,0));

+

+    JPanel x509Panel = new JPanel(new GridBagLayout());

+    x509Panel.setBorder(BorderFactory.createTitledBorder("X.509 certificates"));

+    ca = new JButton("Load CA certificate");

+    ca.setPreferredSize(new Dimension(145,25));

+    ca.addActionListener(this);

+    crl = new JButton("Load CRL certificate");

+    crl.setPreferredSize(new Dimension(145,25));

+    crl.addActionListener(this);

+    addGBComponent(ca, x509Panel,  0, 0, 1, 1, 2, 2, 0, 1, GridBagConstraints.NONE, GridBagConstraints.LINE_START, new Insets(2,2,2,2));

+    addGBComponent(crl, x509Panel, 1, 0, 1, 1, 2, 2, 1, 1, GridBagConstraints.NONE, GridBagConstraints.LINE_START, new Insets(2,2,2,2));

+

+    JPanel authPanel = new JPanel(new GridBagLayout());

+    authPanel.setBorder(BorderFactory.createTitledBorder("Authentication"));

+    secNone = addCheckbox("None", null, authPanel);

+    secVnc = addCheckbox("Standard VNC", null, authPanel);

+    secPlain = addJCheckBox("Plaintext", null, authPanel, new GridBagConstraints(0,2,1,1,1,1,GridBagConstraints.LINE_START,GridBagConstraints.NONE,new Insets(0,0,0,5),0,0));

+    secManaged = addJCheckBox("Managed", null, authPanel, new GridBagConstraints(0,3,1,1,1,1,GridBagConstraints.LINE_START,GridBagConstraints.NONE,new Insets(0,0,0,5),0,0));

+    sendLocalUsername = new JCheckBox("Send Local Username");

+    sendLocalUsername.addItemListener(this);

+    addGBComponent(sendLocalUsername, authPanel, 1, 2, 1, 2, 0, 0, 2, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,20,0,0));

+

+    secVeNCrypt = new JCheckBox("Extended encryption and authentication methods (VeNCrypt)");

+    secVeNCrypt.addItemListener(this);

+    addGBComponent(secVeNCrypt,SecPanel,        0, 0, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,2,0,20));

+    addGBComponent(encryptionPanel,SecPanel, 0, 1, 1, 1, 2, 2, 1, 0, GridBagConstraints.NONE, GridBagConstraints.LINE_START, new Insets(0,4,2,4));

+    addGBComponent(x509Panel,SecPanel,       0, 2, 1, 1, 2, 2, 1, 0, GridBagConstraints.NONE, GridBagConstraints.LINE_START, new Insets(2,4,2,4));

+    addGBComponent(authPanel,SecPanel,       0, 3, 1, 1, 2, 2, 1, 1, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(2,4,2,4));

+

+    tabPane.add(FormatPanel);

+    tabPane.add(InputsPanel);

+    tabPane.add(MiscPanel);

+    tabPane.add(DefaultsPanel);

+    tabPane.add(SecPanel);

+    tabPane.addTab("Colour & Encoding", FormatPanel);

+    tabPane.addTab("Inputs", InputsPanel);

+    tabPane.addTab("Misc", MiscPanel);

+    tabPane.addTab("Load / Save", DefaultsPanel);

+    tabPane.addTab("Security", SecPanel);

+    tabPane.setBorder(BorderFactory.createEmptyBorder(4,4,0,4));

+

+    okButton = new JButton("OK");

+    okButton.setPreferredSize(new Dimension(90,30));

+    okButton.addActionListener(this);

+    cancelButton = new JButton("Cancel");

+    cancelButton.setPreferredSize(new Dimension(90,30));

+    cancelButton.addActionListener(this);

+

+    JPanel buttonPane = new JPanel();

+    buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));

+    buttonPane.setBorder(BorderFactory.createEmptyBorder(4,0,0,0));

+    buttonPane.add(Box.createHorizontalGlue());

+    buttonPane.add(okButton);

+    buttonPane.add(Box.createRigidArea(new Dimension(4,0)));

+    buttonPane.add(cancelButton);

+    buttonPane.add(Box.createRigidArea(new Dimension(4,0)));

+

+    this.getContentPane().add(tabPane);

+    this.getContentPane().add(buttonPane);

+

+    pack();

+	

+  }

+

+  public void initDialog() {

+    if (cb != null) cb.setOptions();

+    zrle.setEnabled(!autoSelect.isSelected());

+    hextile.setEnabled(!autoSelect.isSelected());

+    tight.setEnabled(!autoSelect.isSelected());

+    raw.setEnabled(!autoSelect.isSelected());

+    fullColour.setEnabled(!autoSelect.isSelected());

+    mediumColour.setEnabled(!autoSelect.isSelected());

+    lowColour.setEnabled(!autoSelect.isSelected());

+    veryLowColour.setEnabled(!autoSelect.isSelected());

+    compressLevel.setEnabled(customCompressLevel.isSelected());

+    qualityLevel.setEnabled(noJpeg.isSelected());

+    sendLocalUsername.setEnabled(secVeNCrypt.isEnabled()&&

+      (secPlain.isSelected()||secManaged.isSelected()));

+  }

+

+  JRadioButton addRadioCheckbox(String str, ButtonGroup group, JPanel panel) {

+    JRadioButton c = new JRadioButton(str);

+    GridBagConstraints gbc = new GridBagConstraints();

+    gbc.anchor = GridBagConstraints.LINE_START;

+    gbc.gridwidth = GridBagConstraints.REMAINDER;

+    gbc.weightx = 1;

+    gbc.weighty = 1;

+    panel.add(c,gbc);

+    group.add(c);

+    c.addItemListener(this);

+    return c;

+  }

+

+  JCheckBox addCheckbox(String str, ButtonGroup group, JPanel panel) {

+    JCheckBox c = new JCheckBox(str);

+    GridBagConstraints gbc = new GridBagConstraints();

+    gbc.anchor = GridBagConstraints.LINE_START;

+    gbc.gridwidth = GridBagConstraints.REMAINDER;

+    gbc.weightx = 1;

+    gbc.weighty = 1;

+    panel.add(c,gbc);

+    if (group != null)

+      group.add(c);

+    c.addItemListener(this);

+    return c;

+  }

+

+  JCheckBox addJCheckBox(String str, ButtonGroup group, JPanel panel,

+      GridBagConstraints gbc) {

+    JCheckBox c = new JCheckBox(str);

+    panel.add(c,gbc);

+    if (group != null)

+      group.add(c);

+    c.addItemListener(this);

+    

+    return c;

+  }

+

+  public void actionPerformed(ActionEvent e) {

+    Object s = e.getSource();

+    if (s instanceof JButton && (JButton)s == okButton) {

+      ok = true;

+      if (cb != null) cb.getOptions();

+      endDialog();

+    } else if (s instanceof JButton && (JButton)s == cancelButton) {

+      ok = false;

+      endDialog();

+    } else if (s instanceof JButton && (JButton)s == defSaveButton) {

+      try {

+        defaults.Save();

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

+    } else if (s instanceof JButton && (JButton)s == ca) {

+      JFileChooser fc = new JFileChooser();

+      fc.setDialogTitle("Path to X509 CA certificate");

+      int ret = fc.showOpenDialog(this);

+      if (ret == JFileChooser.APPROVE_OPTION)

+        CSecurityTLS.x509ca.setParam(fc.getSelectedFile().toString());

+    } else if (s instanceof JButton && (JButton)s == crl) {

+      JFileChooser fc = new JFileChooser();

+      fc.setDialogTitle("Path to X509 CRL file");

+      int ret = fc.showOpenDialog(this);

+      if (ret == JFileChooser.APPROVE_OPTION)

+        CSecurityTLS.x509crl.setParam(fc.getSelectedFile().toString());

+    }

+  }

+

+  public void itemStateChanged(ItemEvent e) {

+    Object s = e.getSource();

+    if (s instanceof JCheckBox && (JCheckBox)s == autoSelect) {

+      zrle.setEnabled(!autoSelect.isSelected());

+      hextile.setEnabled(!autoSelect.isSelected());

+      tight.setEnabled(!autoSelect.isSelected());

+      raw.setEnabled(!autoSelect.isSelected());

+      fullColour.setEnabled(!autoSelect.isSelected());

+      mediumColour.setEnabled(!autoSelect.isSelected());

+      lowColour.setEnabled(!autoSelect.isSelected());

+      veryLowColour.setEnabled(!autoSelect.isSelected());

+      defaults.setPref("autoSelect",(autoSelect.isSelected()) ? "on" : "off");

+    } 

+    if (s instanceof JCheckBox && (JCheckBox)s == customCompressLevel) {

+      compressLevel.setEnabled(customCompressLevel.isSelected());

+      defaults.setPref("customCompressLevel",(customCompressLevel.isSelected()) ? "on" : "off");

+    }

+    if (s instanceof JCheckBox && (JCheckBox)s == noJpeg) {

+      qualityLevel.setEnabled(noJpeg.isSelected());

+      defaults.setPref("noJpeg",(noJpeg.isSelected()) ? "on" : "off");

+    }

+    if (s instanceof JCheckBox && (JCheckBox)s == sendLocalUsername) {

+      defaults.setPref("sendLocalUsername",(sendLocalUsername.isSelected()) ? "on" : "off");

+    }

+    if (s instanceof JCheckBox && (JCheckBox)s == secVeNCrypt) {

+      encNone.setEnabled(secVeNCrypt.isSelected());

+      encTLS.setEnabled(secVeNCrypt.isSelected());

+      encX509.setEnabled(secVeNCrypt.isSelected());

+      ca.setEnabled(secVeNCrypt.isSelected());

+      crl.setEnabled(secVeNCrypt.isSelected());

+      secManaged.setEnabled(secVeNCrypt.isSelected());

+      secNone.setEnabled(secVeNCrypt.isSelected());

+      secVnc.setEnabled(secVeNCrypt.isSelected());

+      secPlain.setEnabled(secVeNCrypt.isSelected());

+      sendLocalUsername.setEnabled(secVeNCrypt.isSelected());

+    }

+    if (s instanceof JCheckBox && (JCheckBox)s == secManaged ||

+        s instanceof JCheckBox && (JCheckBox)s == secPlain) {

+      sendLocalUsername.setEnabled(secManaged.isSelected()||secPlain.isSelected());

+    }

+  }

+

+}

diff --git a/java/src/com/tigervnc/vncviewer/MemInStream.java b/java/src/com/tigervnc/vncviewer/OptionsDialogCallback.java
similarity index 70%
copy from java/src/com/tigervnc/vncviewer/MemInStream.java
copy to java/src/com/tigervnc/vncviewer/OptionsDialogCallback.java
index 41a4fc0..f6897e2 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/vncviewer/OptionsDialogCallback.java
@@ -18,17 +18,7 @@
 
 package com.tigervnc.vncviewer;
 
-public class MemInStream extends InStream {
-
-  public MemInStream(byte[] data, int offset, int len) {
-    b = data;
-    ptr = offset;
-    end = offset + len;
-  }
-
-  public int pos() { return ptr; }
-
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    throw new Exception("MemInStream overrun: end of stream");
-  }
+public interface OptionsDialogCallback {
+  public void setOptions();
+  public void getOptions();
 }
diff --git a/java/src/com/tigervnc/vncviewer/OptionsFrame.java b/java/src/com/tigervnc/vncviewer/OptionsFrame.java
deleted file mode 100644
index 573f21d..0000000
--- a/java/src/com/tigervnc/vncviewer/OptionsFrame.java
+++ /dev/null
@@ -1,441 +0,0 @@
-//
-//  Copyright (C) 2001 HorizonLive.com, Inc.  All Rights Reserved.
-//  Copyright (C) 2001 Constantin Kaplinsky.  All Rights Reserved.
-//  Copyright (C) 2000 Tridia Corporation.  All Rights Reserved.
-//  Copyright (C) 1999 AT&T Laboratories Cambridge.  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.
-//
-
-//
-// Options frame.
-//
-// This deals with all the options the user can play with.
-// It sets the encodings array and some booleans.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.awt.*;
-import java.awt.event.*;
-
-class OptionsFrame extends Frame
-  implements WindowListener, ActionListener, ItemListener {
-
-  static String[] names = {
-    "Encoding",
-    "Compression level",
-    "JPEG image quality",
-    "Cursor shape updates",
-    "Use CopyRect",
-    "Restricted colors",
-    "Mouse buttons 2 and 3",
-    "View only",
-    "Scaling factor",
-    "Scale remote cursor",
-    "Share desktop"
-  };
-
-  static String[][] values = {
-    { "Auto", "Raw", "RRE", "CoRRE", "Hextile", "Zlib", "Tight", "ZRLE" },
-    { "Default", "1", "2", "3", "4", "5", "6", "7", "8", "9" },
-    { "JPEG off", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" },
-    { "Enable", "Ignore", "Disable" },
-    { "Yes", "No" },
-    { "Yes", "No" },
-    { "Normal", "Reversed" },
-    { "Yes", "No" },
-    { "Auto", "1%", "5%", "10%", "20%", "25%", "50%", "75%", "100%"},
-    { "No", "50%", "75%", "125%", "150%" },
-    { "Yes", "No" }
-  };
-
-  final int
-    encodingIndex        = 0,
-    compressLevelIndex   = 1,
-    jpegQualityIndex     = 2,
-    cursorUpdatesIndex   = 3,
-    useCopyRectIndex     = 4,
-    eightBitColorsIndex  = 5,
-    mouseButtonIndex     = 6,
-    viewOnlyIndex        = 7,
-    scalingFactorIndex   = 8,
-    scaleCursorIndex     = 9,
-    shareDesktopIndex    = 10;
-
-  Label[] labels = new Label[names.length];
-  Choice[] choices = new Choice[names.length];
-  Button closeButton;
-  VncViewer viewer;
-
-
-  //
-  // The actual data which other classes look at:
-  //
-
-  int preferredEncoding;
-  int compressLevel;
-  int jpegQuality;
-  boolean useCopyRect;
-  boolean requestCursorUpdates;
-  boolean ignoreCursorUpdates;
-
-  boolean eightBitColors;
-
-  boolean reverseMouseButtons2And3;
-  boolean shareDesktop;
-  boolean viewOnly;
-  int scaleCursor;
-
-  boolean autoScale;
-  int scalingFactor;
-
-  //
-  // Constructor.  Set up the labels and choices from the names and values
-  // arrays.
-  //
-
-  OptionsFrame(VncViewer v) {
-    super("TigerVNC Options");
-
-    viewer = v;
-
-    GridBagLayout gridbag = new GridBagLayout();
-    setLayout(gridbag);
-
-    GridBagConstraints gbc = new GridBagConstraints();
-    gbc.fill = GridBagConstraints.BOTH;
-
-    for (int i = 0; i < names.length; i++) {
-      labels[i] = new Label(names[i]);
-      gbc.gridwidth = 1;
-      gridbag.setConstraints(labels[i],gbc);
-      add(labels[i]);
-
-      choices[i] = new Choice();
-      gbc.gridwidth = GridBagConstraints.REMAINDER;
-      gridbag.setConstraints(choices[i],gbc);
-      add(choices[i]);
-      choices[i].addItemListener(this);
-
-      for (int j = 0; j < values[i].length; j++) {
-	choices[i].addItem(values[i][j]);
-      }
-    }
-
-    closeButton = new Button("Close");
-    gbc.gridwidth = GridBagConstraints.REMAINDER;
-    gridbag.setConstraints(closeButton, gbc);
-    add(closeButton);
-    closeButton.addActionListener(this);
-
-    pack();
-
-    addWindowListener(this);
-
-    // Set up defaults
-
-    choices[encodingIndex].select("Auto");
-    choices[compressLevelIndex].select("Default");
-    choices[jpegQualityIndex].select("6");
-    choices[cursorUpdatesIndex].select("Enable");
-    choices[useCopyRectIndex].select("Yes");
-    choices[eightBitColorsIndex].select("No");
-    choices[mouseButtonIndex].select("Normal");
-    choices[viewOnlyIndex].select("No");
-    choices[scaleCursorIndex].select("No");
-    choices[shareDesktopIndex].select("Yes");
-
-    // But let them be overridden by parameters
-
-    for (int i = 0; i < names.length; i++) {
-      String s = viewer.readParameter(names[i], false);
-      if (s != null) {
-	for (int j = 0; j < values[i].length; j++) {
-	  if (s.equalsIgnoreCase(values[i][j])) {
-	    choices[i].select(j);
-	  }
-	}
-      }
-    }
-
-    // Get scaling factor from parameters and set it
-    // to gui and class member scalingFactor
-    
-    String s = viewer.readParameter("Scaling Factor", false);
-    if (s == null) s = "100%";
-    setScalingFactor(s);
-    if (autoScale) {
-      choices[scalingFactorIndex].select("Auto");
-    } else {
-      choices[scalingFactorIndex].select(s);
-    }
-
-    // Make the booleans and encodings array correspond to the state of the GUI
-
-    setEncodings();
-    setColorFormat();
-    setOtherOptions();
-  }
-  
-  //
-  // Set scaling factor class member value
-  //
-  
-  void setScalingFactor(int sf) {
-      setScalingFactor(((Integer)sf).toString());
-  }
-  
-  void setScalingFactor(String s) {
-    autoScale = false;
-    scalingFactor = 100;
-    if (s != null) {
-      if (s.equalsIgnoreCase("Auto")) {
-	autoScale = true;
-      } else {
-	// Remove the '%' char at the end of string if present.
-	if (s.charAt(s.length() - 1) == '%') {
-	  s = s.substring(0, s.length() - 1);
-	}
-	// Convert to an integer.
-	try {
-	  scalingFactor = Integer.parseInt(s);
-	}
-	catch (NumberFormatException e) {
-	  scalingFactor = 100;
-	}
-	// Make sure scalingFactor is in the range of [1..1000].
-	if (scalingFactor < 1) {
-	  scalingFactor = 1;
-	} else if (scalingFactor > 1000) {
-	  scalingFactor = 1000;
-	}
-      }
-    }
-  }
-
-
-  //
-  // Disable the shareDesktop option
-  //
-
-  void disableShareDesktop() {
-    labels[shareDesktopIndex].setEnabled(false);
-    choices[shareDesktopIndex].setEnabled(false);
-  }
-
-  //
-  // setEncodings looks at the encoding, compression level, JPEG
-  // quality level, cursor shape updates and copyRect choices and sets
-  // corresponding variables properly. Then it calls the VncViewer's
-  // setEncodings method to send a SetEncodings message to the RFB
-  // server.
-  //
-
-  void setEncodings() {
-    useCopyRect = choices[useCopyRectIndex].getSelectedItem().equals("Yes");
-
-    preferredEncoding = RfbProto.EncodingRaw;
-    boolean enableCompressLevel = false;
-    boolean enableQualityLevel = false;
-
-    if (choices[encodingIndex].getSelectedItem().equals("RRE")) {
-      preferredEncoding = RfbProto.EncodingRRE;
-    } else if (choices[encodingIndex].getSelectedItem().equals("CoRRE")) {
-      preferredEncoding = RfbProto.EncodingCoRRE;
-    } else if (choices[encodingIndex].getSelectedItem().equals("Hextile")) {
-      preferredEncoding = RfbProto.EncodingHextile;
-    } else if (choices[encodingIndex].getSelectedItem().equals("ZRLE")) {
-      preferredEncoding = RfbProto.EncodingZRLE;
-    } else if (choices[encodingIndex].getSelectedItem().equals("Zlib")) {
-      preferredEncoding = RfbProto.EncodingZlib;
-      enableCompressLevel = true;
-    } else if (choices[encodingIndex].getSelectedItem().equals("Tight")) {
-      preferredEncoding = RfbProto.EncodingTight;
-      enableCompressLevel = true;
-      enableQualityLevel = !eightBitColors;
-    } else if (choices[encodingIndex].getSelectedItem().equals("Auto")) {
-      preferredEncoding = -1;
-      enableQualityLevel = !eightBitColors;
-    }
-
-    // Handle compression level setting.
-
-    try {
-      compressLevel =
-        Integer.parseInt(choices[compressLevelIndex].getSelectedItem());
-    }
-    catch (NumberFormatException e) {
-      compressLevel = -1;
-    }
-    if (compressLevel < 1 || compressLevel > 9) {
-      compressLevel = -1;
-    }
-    labels[compressLevelIndex].setEnabled(enableCompressLevel);
-    choices[compressLevelIndex].setEnabled(enableCompressLevel);
-
-    // Handle JPEG quality setting.
-
-    try {
-      jpegQuality =
-        Integer.parseInt(choices[jpegQualityIndex].getSelectedItem());
-    }
-    catch (NumberFormatException e) {
-      jpegQuality = -1;
-    }
-    if (jpegQuality < 0 || jpegQuality > 9) {
-      jpegQuality = -1;
-    }
-    labels[jpegQualityIndex].setEnabled(enableQualityLevel);
-    choices[jpegQualityIndex].setEnabled(enableQualityLevel);
-
-    // Request cursor shape updates if necessary.
-
-    requestCursorUpdates =
-      !choices[cursorUpdatesIndex].getSelectedItem().equals("Disable");
-
-    if (requestCursorUpdates) {
-      ignoreCursorUpdates =
-	choices[cursorUpdatesIndex].getSelectedItem().equals("Ignore");
-    }
-
-    viewer.setEncodings();
-  }
-
-  //
-  // setColorFormat sets eightBitColors variable depending on the GUI
-  // setting, causing switches between 8-bit and 24-bit colors mode if
-  // necessary.
-  //
-
-  void setColorFormat() {
-
-    eightBitColors =
-      choices[eightBitColorsIndex].getSelectedItem().equals("Yes");
-
-    boolean enableJPEG = !eightBitColors &&
-      (choices[encodingIndex].getSelectedItem().equals("Tight") ||
-       choices[encodingIndex].getSelectedItem().equals("Auto"));
-
-    labels[jpegQualityIndex].setEnabled(enableJPEG);
-    choices[jpegQualityIndex].setEnabled(enableJPEG);
-  }
-
-  //
-  // setOtherOptions looks at the "other" choices (ones that do not
-  // cause sending any protocol messages) and sets the boolean flags
-  // appropriately.
-  //
-
-  void setOtherOptions() {
-
-    reverseMouseButtons2And3
-      = choices[mouseButtonIndex].getSelectedItem().equals("Reversed");
-
-    viewOnly 
-      = choices[viewOnlyIndex].getSelectedItem().equals("Yes");
-    if (viewer.vc != null)
-      viewer.vc.enableInput(!viewOnly);
-
-    shareDesktop
-      = choices[shareDesktopIndex].getSelectedItem().equals("Yes");
-
-    String scaleString = choices[scaleCursorIndex].getSelectedItem();
-    if (scaleString.endsWith("%"))
-      scaleString = scaleString.substring(0, scaleString.length() - 1);
-    try {
-      scaleCursor = Integer.parseInt(scaleString);
-    }
-    catch (NumberFormatException e) {
-      scaleCursor = 0;
-    }
-    if (scaleCursor < 10 || scaleCursor > 500) {
-      scaleCursor = 0;
-    }
-    if (requestCursorUpdates && !ignoreCursorUpdates && !viewOnly) {
-      labels[scaleCursorIndex].setEnabled(true);
-      choices[scaleCursorIndex].setEnabled(true);
-    } else {
-      labels[scaleCursorIndex].setEnabled(false);
-      choices[scaleCursorIndex].setEnabled(false);
-    }
-    if (viewer.vc != null)
-      viewer.vc.createSoftCursor(); // update cursor scaling
-  }
-
-
-  //
-  // Respond to actions on Choice controls
-  //
-
-  public void itemStateChanged(ItemEvent evt) {
-    Object source = evt.getSource();
-
-    if (source == choices[encodingIndex] ||
-        source == choices[compressLevelIndex] ||
-        source == choices[jpegQualityIndex] ||
-        source == choices[cursorUpdatesIndex] ||
-        source == choices[useCopyRectIndex]) {
-
-      setEncodings();
-
-      if (source == choices[cursorUpdatesIndex]) {
-        setOtherOptions();      // update scaleCursor state
-      }
-
-    } else if (source == choices[eightBitColorsIndex]) {
-
-      setColorFormat();
-
-    } else if (source == choices[mouseButtonIndex] ||
-	       source == choices[shareDesktopIndex] ||
-	       source == choices[viewOnlyIndex] ||
-	       source == choices[scaleCursorIndex]) {
-
-      setOtherOptions();
-
-    } else if (source == choices[scalingFactorIndex]){
-        // Tell VNC canvas that scaling factor has changed
-        setScalingFactor(choices[scalingFactorIndex].getSelectedItem());
-        if (viewer.vc != null)
-          viewer.vc.setScalingFactor(scalingFactor);
-    }
-  }
-
-  //
-  // Respond to button press
-  //
-
-  public void actionPerformed(ActionEvent evt) {
-    if (evt.getSource() == closeButton)
-      setVisible(false);
-  }
-
-  //
-  // Respond to window events
-  //
-
-  public void windowClosing(WindowEvent evt) {
-    setVisible(false);
-  }
-
-  public void windowActivated(WindowEvent evt) {}
-  public void windowDeactivated(WindowEvent evt) {}
-  public void windowOpened(WindowEvent evt) {}
-  public void windowClosed(WindowEvent evt) {}
-  public void windowIconified(WindowEvent evt) {}
-  public void windowDeiconified(WindowEvent evt) {}
-}
diff --git a/java/src/com/tigervnc/vncviewer/PasswdDialog.java b/java/src/com/tigervnc/vncviewer/PasswdDialog.java
new file mode 100644
index 0000000..eaace69
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/PasswdDialog.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.
+ */
+
+package com.tigervnc.vncviewer;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.net.URL;
+
+class PasswdDialog extends Dialog implements KeyListener{
+
+  public PasswdDialog(String title, boolean userDisabled, boolean passwdDisabled) {
+    super(true);
+    setResizable(false);
+    setTitle(title);
+    setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+
+    JPanel p1 = new JPanel();
+    userLabel = new JLabel("Username:");
+    p1.add(userLabel);
+    userEntry = new JTextField(30);
+    userEntry.setEnabled(!userDisabled);
+    userLabel.setEnabled(!userDisabled);
+    p1.add(userEntry);
+    userEntry.addKeyListener(this);
+
+    JPanel p2 = new JPanel();
+    passwdLabel = new JLabel("Password:");
+    passwdLabel.setPreferredSize(userLabel.getPreferredSize());
+    p2.add(passwdLabel);
+    passwdEntry = new JPasswordField(30);
+    passwdEntry.setEnabled(!passwdDisabled);
+    passwdLabel.setEnabled(!passwdDisabled);
+    p2.add(passwdEntry);
+    passwdEntry.addKeyListener(this);
+
+    getContentPane().setLayout(new BoxLayout(getContentPane(),BoxLayout.Y_AXIS));
+    getContentPane().add(p1);
+    getContentPane().add(p2);
+    pack();
+    if (userEntry.isEnabled()) {
+      userEntry.requestFocus();
+    } else {
+      passwdEntry.requestFocus();
+    }
+  }
+
+  /** Handle the key-typed event. */
+  public void keyTyped(KeyEvent event) { }
+  /** Handle the key-released event. */
+  public void keyReleased(KeyEvent event) { }
+  /** Handle the key-pressed event. */
+  public void keyPressed(KeyEvent event) {
+    Object s = event.getSource();
+    if (s instanceof JTextField && (JTextField)s == userEntry) {
+       if (event.getKeyCode() == KeyEvent.VK_ENTER) {
+         ok = true;
+         endDialog();
+        }
+    } else if (s instanceof JPasswordField && (JPasswordField)s == passwdEntry) {
+        if (event.getKeyCode() == KeyEvent.VK_ENTER) {
+         ok = true;
+         endDialog();
+        }
+    }
+  }
+
+  JLabel userLabel;
+  JTextField userEntry;
+  JLabel passwdLabel;
+  JTextField passwdEntry;
+}
diff --git a/java/src/com/tigervnc/vncviewer/PixelBufferImage.java b/java/src/com/tigervnc/vncviewer/PixelBufferImage.java
new file mode 100644
index 0000000..7e5e717
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/PixelBufferImage.java
@@ -0,0 +1,185 @@
+/* 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.
+ */
+
+//
+// PixelBufferImage is an PixelBuffer which also acts as an ImageProducer.
+// Currently it only supports 8-bit colourmapped pixel format.
+//
+
+package com.tigervnc.vncviewer;
+
+import java.awt.*;
+import java.awt.image.*;
+import java.nio.ByteOrder;
+import javax.swing.JScrollPane;
+
+import com.tigervnc.rfb.*;
+
+public class PixelBufferImage extends PixelBuffer implements ImageProducer
+{
+  public PixelBufferImage(int w, int h, CConn cc_, DesktopWindow desktop_) {
+    cc = cc_;
+    desktop = desktop_;
+    PixelFormat nativePF = getNativePF();
+    switch ((nativePF.depth > cc.serverPF.depth) ? cc.serverPF.depth : nativePF.depth) {
+    case  8: 
+      setPF(new PixelFormat(8,8,false,true,7,7,3,0,3,6));
+      break;
+    case 16: 
+      setPF(new PixelFormat(16,16,false,true,0xF800,0x07C0,0x003E,0,0,0));
+      break;
+    case 24: 
+      setPF(new PixelFormat(32,24,false,true,0xff,0xff,0xff,16,8,0));
+      break;
+    default:
+      setPF(new PixelFormat(8,8,false,true,7,7,3,0,3,6));
+      vlog.debug("Unsupported native PF, defaulting to depth 8");
+    }
+    resize(w, h);
+  }
+
+  // resize() resizes the image, preserving the image data where possible.
+  public void resize(int w, int h) {
+    if (w == width() && h == height()) return;
+
+    int rowsToCopy = h < height() ? h : height();
+    int copyWidth = w < width() ? w : width();
+    int oldWidth = width();
+    int[] oldData = data;
+
+    width_ = w;
+    height_ = h;
+    image = desktop.createImage(this);
+    //image.setAccelerationPriority(1);
+
+    data = new int[width() * height()];
+
+    for (int i = 0; i < rowsToCopy; i++)
+      System.arraycopy(oldData, copyWidth * i,
+                       data, width() * i, copyWidth);
+  }
+
+  private PixelFormat getNativePF() {
+    PixelFormat pf;
+    cm = java.awt.Toolkit.getDefaultToolkit().getColorModel();
+    if (cm.getColorSpace().getType() == java.awt.color.ColorSpace.TYPE_RGB) {
+      int depth = cm.getPixelSize();
+      int bpp = (depth > 16 ? 32 : (depth > 8 ? 16 : 8));
+      ByteOrder byteOrder = ByteOrder.nativeOrder();
+      boolean bigEndian = (byteOrder == ByteOrder.BIG_ENDIAN ? true : false);
+      boolean trueColour = (depth > 8 ? true : false);
+      int redShift    = cm.getComponentSize()[0] + cm.getComponentSize()[1];
+      int greenShift  = cm.getComponentSize()[0];
+      int blueShift   = 0;
+      pf = new PixelFormat(bpp, depth, bigEndian, trueColour,
+        (depth > 8 ? 0xff : 0),
+        (depth > 8 ? 0xff : 0),
+        (depth > 8 ? 0xff : 0),
+        (depth > 8 ? redShift : 0),
+        (depth > 8 ? greenShift : 0),
+        (depth > 8 ? blueShift : 0));
+    } else {
+      pf = new PixelFormat(8, 8, false, false, 7, 7, 3, 0, 3, 6);
+    }
+    vlog.debug("Native pixel format is "+pf.print());
+    return pf;
+  }
+
+  // put() causes the given rectangle to be drawn using the given graphics
+  // context.
+  public void put(int x, int y, int w, int h, Graphics g) {
+    if (ic != null) {
+      ic.setPixels(x, y, w, h, cm, data, width() * y + x, width());
+      ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE);
+    }
+  }
+
+  // fillRect(), imageRect(), maskRect() are inherited from PixelBuffer.  For
+  // copyRect() we also need to tell the ImageConsumer that the pixels have
+  // changed (this is done in the put() call for the others).
+
+  public void copyRect(int x, int y, int w, int h, int srcX, int srcY) {
+    super.copyRect(x, y, w, h, srcX, srcY);
+    if (ic == null) return;
+    ic.setPixels(x, y, w, h, cm, data, width() * y + x, width());
+    ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE);
+  }
+
+  // setColourMapEntries() changes some of the entries in the colourmap.
+  // However these settings won't take effect until updateColourMap() is
+  // called.  This is because getting java to recalculate its internal
+  // translation table and redraw the screen is expensive.
+
+  public void setColourMapEntries(int firstColour, int nColours_,
+                                               int[] rgbs) {
+    nColours = nColours_;
+    reds = new byte[nColours];
+    blues = new byte[nColours];
+    greens = new byte[nColours];
+    for (int i = 0; i < nColours; i++) {
+      reds[firstColour+i] = (byte)(rgbs[i*3]   >> 8);
+      greens[firstColour+i] = (byte)(rgbs[i*3+1] >> 8);
+      blues[firstColour+i] = (byte)(rgbs[i*3+2] >> 8);
+    }
+  }
+
+  // ImageProducer methods
+
+  public void updateColourMap() {
+    cm = new IndexColorModel(8, nColours, reds, greens, blues);
+  }
+
+  public void addConsumer(ImageConsumer c) {
+    if (ic == c) return;
+    
+    vlog.debug("adding consumer "+c);
+    
+    if (ic != null)
+      vlog.error("Only one ImageConsumer allowed - discarding old one");
+    
+    ic = c;
+    ic.setDimensions(width(), height());
+    ic.setHints(ImageConsumer.RANDOMPIXELORDER);
+    // Calling ic.setColorModel(cm) seemed to help in some earlier versions of
+    // the JDK, but it shouldn't be necessary because we pass the ColorModel
+    // with each setPixels() call.
+    ic.setPixels(0, 0, width(), height(), cm, data, 0, width());
+    ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE);
+  }
+
+  public void removeConsumer(ImageConsumer c) {
+    System.err.println("removeConsumer "+c);
+    if (ic == c) ic = null;
+  }
+
+  public boolean isConsumer(ImageConsumer c) { return ic == c; }
+  public void requestTopDownLeftRightResend(ImageConsumer c) {}
+  public void startProduction(ImageConsumer c) { addConsumer(c); }
+
+  Image image;
+  ImageConsumer ic;
+
+  int nColours;
+  byte[] reds;
+  byte[] greens;
+  byte[] blues;
+
+  CConn cc;
+  DesktopWindow desktop;
+  static LogWriter vlog = new LogWriter("PixelBufferImage");
+}
diff --git a/java/src/com/tigervnc/vncviewer/README b/java/src/com/tigervnc/vncviewer/README
index 39ba825..c6949d5 100644
--- a/java/src/com/tigervnc/vncviewer/README
+++ b/java/src/com/tigervnc/vncviewer/README
@@ -9,7 +9,7 @@
 
 	Copyright (C) 1999 AT&T Laboratories Cambridge.
 	Copyright (C) 2000 Tridia Corp.
-	Copyright (C) 2002-2003 RealVNC Ltd.
+	Copyright (C) 2002-2005 RealVNC Ltd.
 	Copyright (C) 2001-2004 HorizonLive.com, Inc.
 	Copyright (C) 2000-2007 Constantin Kaplinsky
 	Copyright (C) 2000-2007 TightVNC Group
diff --git a/java/src/com/tigervnc/vncviewer/RecordOutputStream.java b/java/src/com/tigervnc/vncviewer/RecordOutputStream.java
deleted file mode 100644
index 7f13249..0000000
--- a/java/src/com/tigervnc/vncviewer/RecordOutputStream.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.tigervnc.vncviewer;
-
-import java.io.DataOutput;
-import java.io.IOException;
-
-public class RecordOutputStream implements DataOutput {
-
-  public RecordOutputStream(RfbProto rfbproto) {
-    rfb = rfbproto;
-  }
-
-  private boolean canWrite() {
-    return ((rfb != null) && (rfb.rec != null));
-  }
-
-  public void write(byte[] b) throws IOException {
-    if (canWrite())
-      rfb.rec.write(b);
-  }
-
-  public void write(byte[] b, int off, int len) throws IOException {
-    if (canWrite())
-      rfb.rec.write(b, off, len);
-  }
-
-  public void write(int b) throws IOException {
-    if (canWrite())
-      rfb.rec.writeIntBE(b);
-  }
-
-  public void writeBoolean(boolean v) { }
-
-  public void writeByte(int v) throws IOException {
-    if (canWrite()) {
-      rfb.rec.writeByte(v);
-    }
-  }
-
-  public void writeBytes(String s) { }
-  public void writeChar(int v) { }
-  public void writeChars(String s) { }
-  public void writeDouble(double v) { }
-  public void writeFloat(float v) { }
-
-  public void writeInt(int v) throws IOException {
-    if (canWrite())
-      rfb.rec.writeIntBE(v);
-  }
-
-  public void writeLong(long v) { }
-
-  public void writeShort(int v) throws IOException {
-    if (canWrite())
-      rfb.rec.writeShortBE(v);
-  }
-
-  public void writeUTF(String str) { }
-
-  private RfbProto rfb = null;
-}
diff --git a/java/src/com/tigervnc/vncviewer/RecordingFrame.java b/java/src/com/tigervnc/vncviewer/RecordingFrame.java
deleted file mode 100644
index 6bf40ea..0000000
--- a/java/src/com/tigervnc/vncviewer/RecordingFrame.java
+++ /dev/null
@@ -1,313 +0,0 @@
-//
-//  Copyright (C) 2002 Constantin Kaplinsky.  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.
-//
-
-//
-// Recording frame. It allows to control recording RFB sessions into
-// FBS (FrameBuffer Stream) files.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.io.*;
-import java.awt.*;
-import java.awt.event.*;
-
-class RecordingFrame extends Frame
-  implements WindowListener, ActionListener {
-
-  boolean recording;
-
-  TextField fnameField;
-  Button browseButton;
-
-  Label statusLabel;
-
-  Button recordButton, nextButton, closeButton;
-  VncViewer viewer;
-
-  //
-  // Check if current security manager allows to create a
-  // RecordingFrame object.
-  //
-
-  public static boolean checkSecurity() {
-    SecurityManager security = System.getSecurityManager();
-    if (security != null) {
-      try {
-	security.checkPropertyAccess("user.dir");
-	security.checkPropertyAccess("file.separator");
-	// Work around (rare) checkPropertyAccess bug
-	System.getProperty("user.dir");
-      } catch (SecurityException e) {
-	System.out.println("SecurityManager restricts session recording.");
-	return false;
-      }
-    }
-    return true;
-  }
-
-  //
-  // Constructor.
-  //
-
-  RecordingFrame(VncViewer v) {
-    super("TigerVNC Session Recording");
-
-    viewer = v;
-
-    // Determine initial filename for next saved session.
-    // FIXME: Check SecurityManager.
-
-    String fname = nextNewFilename(System.getProperty("user.dir") +
-				   System.getProperty("file.separator") +
-				   "vncsession.fbs");
-
-    // Construct new panel with file name field and "Browse" button.
-
-    Panel fnamePanel = new Panel();
-    GridBagLayout fnameGridbag = new GridBagLayout();
-    fnamePanel.setLayout(fnameGridbag);
-
-    GridBagConstraints fnameConstraints = new GridBagConstraints();
-    fnameConstraints.gridwidth = GridBagConstraints.RELATIVE;
-    fnameConstraints.fill = GridBagConstraints.BOTH;
-    fnameConstraints.weightx = 4.0;
-
-    fnameField = new TextField(fname, 64);
-    fnameGridbag.setConstraints(fnameField, fnameConstraints);
-    fnamePanel.add(fnameField);
-    fnameField.addActionListener(this);
-
-    fnameConstraints.gridwidth = GridBagConstraints.REMAINDER;
-    fnameConstraints.weightx = 1.0;
-
-    browseButton = new Button("Browse");
-    fnameGridbag.setConstraints(browseButton, fnameConstraints);
-    fnamePanel.add(browseButton);
-    browseButton.addActionListener(this);
-
-    // Construct the frame.
-
-    GridBagLayout gridbag = new GridBagLayout();
-    setLayout(gridbag);
-
-    GridBagConstraints gbc = new GridBagConstraints();
-    gbc.gridwidth = GridBagConstraints.REMAINDER;
-    gbc.fill = GridBagConstraints.BOTH;
-    gbc.weighty = 1.0;
-    gbc.insets = new Insets(10, 0, 0, 0);
-
-    Label helpLabel =
-      new Label("File name to save next recorded session in:", Label.CENTER);
-    gridbag.setConstraints(helpLabel, gbc);
-    add(helpLabel);
-
-    gbc.fill = GridBagConstraints.HORIZONTAL;
-    gbc.weighty = 0.0;
-    gbc.insets = new Insets(0, 0, 0, 0);
-
-    gridbag.setConstraints(fnamePanel, gbc);
-    add(fnamePanel);
-
-    gbc.fill = GridBagConstraints.BOTH;
-    gbc.weighty = 1.0;
-    gbc.insets = new Insets(10, 0, 10, 0);
-
-    statusLabel = new Label("", Label.CENTER);
-    gridbag.setConstraints(statusLabel, gbc);
-    add(statusLabel);
-
-    gbc.fill = GridBagConstraints.HORIZONTAL;
-    gbc.weightx = 1.0;
-    gbc.weighty = 0.0;
-    gbc.gridwidth = 1;
-    gbc.insets = new Insets(0, 0, 0, 0);
-
-    recordButton = new Button("Record");
-    gridbag.setConstraints(recordButton, gbc);
-    add(recordButton);
-    recordButton.addActionListener(this);
-
-    nextButton = new Button("Next file");
-    gridbag.setConstraints(nextButton, gbc);
-    add(nextButton);
-    nextButton.addActionListener(this);
-
-    closeButton = new Button("Close");
-    gridbag.setConstraints(closeButton, gbc);
-    add(closeButton);
-    closeButton.addActionListener(this);
-
-    // Set correct text, font and color for the statusLabel.
-    stopRecording();
-
-    pack();
-
-    addWindowListener(this);
-  }
-
-  //
-  // If the given string ends with ".NNN" where NNN is a decimal
-  // number, increase this number by one. Otherwise, append ".001"
-  // to the given string.
-  //
-
-  protected String nextFilename(String fname) {
-    int len = fname.length();
-    int suffixPos = len;
-    int suffixNum = 1;
-
-    if (len > 4 && fname.charAt(len - 4) == '.') {
-      try {
-	suffixNum = Integer.parseInt(fname.substring(len - 3, len)) + 1;
-	suffixPos = len - 4;
-      } catch (NumberFormatException e) { }
-    }
-
-    char[] zeroes = {'0', '0', '0'};
-    String suffix = String.valueOf(suffixNum);
-    if (suffix.length() < 3) {
-      suffix = new String(zeroes, 0, 3 - suffix.length()) + suffix;
-    }
-
-    return fname.substring(0, suffixPos) + '.' + suffix;
-  }
-
-  //
-  // Find next name of a file which does not exist yet.
-  //
-
-  protected String nextNewFilename(String fname) {
-    String newName = fname;
-    File f;
-    try {
-      do {
-	newName = nextFilename(newName);
-	f = new File(newName);
-      } while (f.exists());
-    } catch (SecurityException e) { }
-
-    return newName;
-  }
-
-  //
-  // Let the user choose a file name showing a FileDialog.
-  //
-
-  protected boolean browseFile() {
-    File currentFile = new File(fnameField.getText());
-
-    FileDialog fd =
-      new FileDialog(this, "Save next session as...", FileDialog.SAVE);
-    fd.setDirectory(currentFile.getParent());
-    fd.setVisible(true);
-    if (fd.getFile() != null) {
-      String newDir = fd.getDirectory();
-      String sep = System.getProperty("file.separator");
-      if (newDir.length() > 0) {
-	if (!sep.equals(newDir.substring(newDir.length() - sep.length())))
-	  newDir += sep;
-      }
-      String newFname = newDir + fd.getFile();
-      if (newFname.equals(fnameField.getText())) {
-	fnameField.setText(newFname);
-	return true;
-      }
-    }
-    return false;
-  }
-
-  //
-  // Start recording.
-  //
-
-  public void startRecording() {
-    statusLabel.setText("Status: Recording...");
-    statusLabel.setFont(new Font("Helvetica", Font.BOLD, 12));
-    statusLabel.setForeground(Color.red);
-    recordButton.setLabel("Stop recording");
-
-    recording = true;
-
-    viewer.setRecordingStatus(fnameField.getText());
-  }
-
-  //
-  // Stop recording.
-  //
-
-  public void stopRecording() {
-    statusLabel.setText("Status: Not recording.");
-    statusLabel.setFont(new Font("Helvetica", Font.PLAIN, 12));
-    statusLabel.setForeground(Color.black);
-    recordButton.setLabel("Record");
-
-    recording = false;
-
-    viewer.setRecordingStatus(null);
-  }
-
-  //
-  // Close our window properly.
-  //
-
-  public void windowClosing(WindowEvent evt) {
-    setVisible(false);
-  }
-
-  //
-  // Ignore window events we're not interested in.
-  //
-
-  public void windowActivated(WindowEvent evt) {}
-  public void windowDeactivated (WindowEvent evt) {}
-  public void windowOpened(WindowEvent evt) {}
-  public void windowClosed(WindowEvent evt) {}
-  public void windowIconified(WindowEvent evt) {}
-  public void windowDeiconified(WindowEvent evt) {}
-
-
-  //
-  // Respond to button presses
-  //
-
-  public void actionPerformed(ActionEvent evt) {
-    if (evt.getSource() == browseButton) {
-      if (browseFile() && recording)
-	startRecording();
-
-    } else if (evt.getSource() == recordButton) {
-      if (!recording) {
-	startRecording();
-      } else {
-	stopRecording();
-        fnameField.setText(nextNewFilename(fnameField.getText()));
-      }
-
-    } else if (evt.getSource() == nextButton) {
-      fnameField.setText(nextNewFilename(fnameField.getText()));
-      if (recording)
-	startRecording();
-
-    } else if (evt.getSource() == closeButton) {
-      setVisible(false);
-
-    }
-  }
-}
diff --git a/java/src/com/tigervnc/vncviewer/ReloginPanel.java b/java/src/com/tigervnc/vncviewer/ReloginPanel.java
deleted file mode 100644
index 51d9d21..0000000
--- a/java/src/com/tigervnc/vncviewer/ReloginPanel.java
+++ /dev/null
@@ -1,66 +0,0 @@
-//
-//  Copyright (C) 2002 Cendio Systems.  All Rights Reserved.
-//  Copyright (C) 2002 Constantin Kaplinsky.  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.
-//
-
-//
-// ReloginPanel class implements panel with a button for logging in again,
-// after fatal errors or disconnect
-//
-
-package com.tigervnc.vncviewer;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.applet.*;
-
-//
-// The panel which implements the Relogin button
-//
-
-class ReloginPanel extends Panel implements ActionListener {
-  Button reloginButton;
-  Button closeButton;
-  VncViewer viewer;
-
-  //
-  // Constructor.
-  //
-  public ReloginPanel(VncViewer v) {
-    viewer = v;
-    setLayout(new FlowLayout(FlowLayout.CENTER));
-    reloginButton = new Button("Login again");
-    add(reloginButton);
-    reloginButton.addActionListener(this);
-    if (viewer.inSeparateFrame) {
-      closeButton = new Button("Close window");
-      add(closeButton);
-      closeButton.addActionListener(this);
-    }
-  }
-
-  //
-  // This method is called when a button is pressed.
-  //
-  public synchronized void actionPerformed(ActionEvent evt) {
-    if (viewer.inSeparateFrame)
-      viewer.vncFrame.dispose();
-    if (evt.getSource() == reloginButton)
-      viewer.getAppletContext().showDocument(viewer.getDocumentBase());
-  }
-}
diff --git a/java/src/com/tigervnc/vncviewer/RfbInputStream.java b/java/src/com/tigervnc/vncviewer/RfbInputStream.java
deleted file mode 100644
index cac3ec7..0000000
--- a/java/src/com/tigervnc/vncviewer/RfbInputStream.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.tigervnc.vncviewer;
-
-import java.io.IOException;
-
-//
-// This class is layer between data of private RfbProto class
-// and classes in other packages.
-//
-// For now this class is used by com.tigervnc.decoder.RawDecoder
-//
-public class RfbInputStream {
-  RfbInputStream(RfbProto rfbProto) {
-    rfb = rfbProto;
-  }
-
-  //
-  // Read data methods
-  //
-
-  public void readFully(byte b[]) throws IOException {
-    readFully(b, 0, b.length);
-  }
-
-  public void readFully(byte b[], int off, int len) throws IOException {
-    rfb.readFully(b, off, len);
-  }
-
-  public int readU32() throws IOException  {
-    return rfb.readU32();
-  }
-
-  public int readU8() throws IOException  {
-    return rfb.readU8();
-  }
-
-  public int readCompactLen() throws IOException {
-    return rfb.readCompactLen();
-  }
-
-  public int readU16() throws IOException {
-    return rfb.readU16();
-  }
-
-  private RfbProto rfb = null;
-}
diff --git a/java/src/com/tigervnc/vncviewer/RfbProto.java b/java/src/com/tigervnc/vncviewer/RfbProto.java
deleted file mode 100644
index da54c56..0000000
--- a/java/src/com/tigervnc/vncviewer/RfbProto.java
+++ /dev/null
@@ -1,1202 +0,0 @@
-//
-//  Copyright (C) 2001-2004 HorizonLive.com, Inc.  All Rights Reserved.
-//  Copyright (C) 2001-2006 Constantin Kaplinsky.  All Rights Reserved.
-//  Copyright (C) 2000 Tridia Corporation.  All Rights Reserved.
-//  Copyright (C) 1999 AT&T Laboratories Cambridge.  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.
-//
-
-//
-// RfbProto.java
-//
-
-package com.tigervnc.vncviewer;
-
-import java.io.*;
-import java.awt.*;
-import java.awt.event.*;
-import java.net.Socket;
-import java.util.zip.*;
-
-class RfbProto {
-
-  final static String
-    versionMsg_3_3 = "RFB 003.003\n",
-    versionMsg_3_7 = "RFB 003.007\n",
-    versionMsg_3_8 = "RFB 003.008\n";
-
-  // Security types
-  final static int
-    SecTypeInvalid   = 0,
-    SecTypeNone      = 1,
-    SecTypeVncAuth   = 2,
-    SecTypeTight     = 16,
-    SecTypeVeNCrypt  = 19,
-    SecTypePlain     = 256,
-    SecTypeTLSNone   = 257,
-    SecTypeTLSVnc    = 258,
-    SecTypeTLSPlain  = 259,
-    SecTypeX509None  = 260,
-    SecTypeX509Vnc   = 261,
-    SecTypeX509Plain = 262;
-
-  // VNC authentication results
-  final static int
-    VncAuthOK      = 0,
-    VncAuthFailed  = 1,
-    VncAuthTooMany = 2;
-
-  // Standard server-to-client messages
-  final static int
-    FramebufferUpdate   = 0,
-    SetColourMapEntries = 1,
-    Bell                = 2,
-    ServerCutText       = 3;
-
-  // Standard client-to-server messages
-  final static int
-    SetPixelFormat           = 0,
-    FixColourMapEntries      = 1,
-    SetEncodings             = 2,
-    FramebufferUpdateRequest = 3,
-    KeyboardEvent            = 4,
-    PointerEvent             = 5,
-    ClientCutText            = 6;
-
-  // Supported encodings and pseudo-encodings
-  final static int
-    EncodingRaw            = 0,
-    EncodingCopyRect       = 1,
-    EncodingRRE            = 2,
-    EncodingCoRRE          = 4,
-    EncodingHextile        = 5,
-    EncodingZlib           = 6,
-    EncodingTight          = 7,
-    EncodingZRLE           = 16,
-    EncodingCompressLevel0 = 0xFFFFFF00,
-    EncodingQualityLevel0  = 0xFFFFFFE0,
-    EncodingXCursor        = 0xFFFFFF10,
-    EncodingRichCursor     = 0xFFFFFF11,
-    EncodingPointerPos     = 0xFFFFFF18,
-    EncodingLastRect       = 0xFFFFFF20,
-    EncodingNewFBSize      = 0xFFFFFF21;
-
-  final static int MaxNormalEncoding = 255;
-
-  // Contstants used in the Hextile decoder
-  final static int
-    HextileRaw                 = 1,
-    HextileBackgroundSpecified = 2,
-    HextileForegroundSpecified = 4,
-    HextileAnySubrects         = 8,
-    HextileSubrectsColoured    = 16;
-
-  // Contstants used in the Tight decoder
-  final static int TightMinToCompress = 12;
-  final static int
-    TightExplicitFilter = 0x04,
-    TightFill           = 0x08,
-    TightJpeg           = 0x09,
-    TightMaxSubencoding = 0x09,
-    TightFilterCopy     = 0x00,
-    TightFilterPalette  = 0x01,
-    TightFilterGradient = 0x02;
-
-
-  String host;
-  int port;
-  Socket sock;
-  OutputStream os;
-  SessionRecorder rec;
-  boolean inNormalProtocol = false;
-  VncViewer viewer;
-
-  // Input stream is declared private to make sure it can be accessed
-  // only via RfbProto methods. We have to do this because we want to
-  // count how many bytes were read.
-  private DataInputStream is;
-  private long numBytesRead = 0;
-  public long getNumBytesRead() { return numBytesRead; }
-
-  // Java on UNIX does not call keyPressed() on some keys, for example
-  // swedish keys To prevent our workaround to produce duplicate
-  // keypresses on JVMs that actually works, keep track of if
-  // keyPressed() for a "broken" key was called or not.
-  boolean brokenKeyPressed = false;
-
-  // This will be set to true on the first framebuffer update
-  // containing Zlib-, ZRLE- or Tight-encoded data.
-  boolean wereZlibUpdates = false;
-
-  // This fields are needed to show warnings about inefficiently saved
-  // sessions only once per each saved session file.
-  boolean zlibWarningShown;
-  boolean tightWarningShown;
-
-  // Before starting to record each saved session, we set this field
-  // to 0, and increment on each framebuffer update. We don't flush
-  // the SessionRecorder data into the file before the second update.
-  // This allows us to write initial framebuffer update with zero
-  // timestamp, to let the player show initial desktop before
-  // playback.
-  int numUpdatesInSession;
-
-  // Measuring network throughput.
-  boolean timing;
-  long timeWaitedIn100us;
-  long timedKbits;
-
-  // Protocol version and TightVNC-specific protocol options.
-  int serverMajor, serverMinor;
-  int clientMajor, clientMinor;
-
-  // If true, informs that the RFB socket was closed.
-  private boolean closed;
-
-  //
-  // Constructor. Make TCP connection to RFB server.
-  //
-
-  RfbProto(String h, int p, VncViewer v) throws IOException {
-    viewer = v;
-    host = h;
-    port = p;
-
-    if (viewer.socketFactory == null) {
-      sock = new Socket(host, port);
-      sock.setTcpNoDelay(true);
-    } else {
-      try {
-	Class factoryClass = Class.forName(viewer.socketFactory);
-	SocketFactory factory = (SocketFactory)factoryClass.newInstance();
-	if (viewer.inAnApplet)
-	  sock = factory.createSocket(host, port, viewer);
-	else
-	  sock = factory.createSocket(host, port, viewer.mainArgs);
-      } catch(Exception e) {
-	e.printStackTrace();
-	throw new IOException(e.getMessage());
-      }
-    }
-    is = new DataInputStream(new BufferedInputStream(sock.getInputStream(),
-						     16384));
-    os = sock.getOutputStream();
-
-    timing = false;
-    timeWaitedIn100us = 5;
-    timedKbits = 0;
-  }
-
-
-  synchronized void close() {
-    try {
-      sock.close();
-      closed = true;
-      System.out.println("RFB socket closed");
-      if (rec != null) {
-	rec.close();
-	rec = null;
-      }
-    } catch (Exception e) {
-      e.printStackTrace();
-    }
-  }
-
-  synchronized boolean closed() {
-    return closed;
-  }
-
-  //
-  // Read server's protocol version message
-  //
-
-  void readVersionMsg() throws Exception {
-
-    byte[] b = new byte[12];
-
-    readFully(b);
-
-    if ((b[0] != 'R') || (b[1] != 'F') || (b[2] != 'B') || (b[3] != ' ')
-	|| (b[4] < '0') || (b[4] > '9') || (b[5] < '0') || (b[5] > '9')
-	|| (b[6] < '0') || (b[6] > '9') || (b[7] != '.')
-	|| (b[8] < '0') || (b[8] > '9') || (b[9] < '0') || (b[9] > '9')
-	|| (b[10] < '0') || (b[10] > '9') || (b[11] != '\n'))
-    {
-      throw new Exception("Host " + host + " port " + port +
-			  " is not an RFB server");
-    }
-
-    serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0');
-    serverMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0');
-
-    if (serverMajor < 3) {
-      throw new Exception("RFB server does not support protocol version 3");
-    }
-  }
-
-
-  //
-  // Write our protocol version message
-  //
-
-  void writeVersionMsg() throws IOException {
-    clientMajor = 3;
-    if (serverMajor > 3 || serverMinor >= 8) {
-      clientMinor = 8;
-      os.write(versionMsg_3_8.getBytes());
-    } else if (serverMinor >= 7) {
-      clientMinor = 7;
-      os.write(versionMsg_3_7.getBytes());
-    } else {
-      clientMinor = 3;
-      os.write(versionMsg_3_3.getBytes());
-    }
-  }
-
-
-  //
-  // Negotiate the authentication scheme.
-  //
-
-  int negotiateSecurity() throws Exception {
-    return (clientMinor >= 7) ?
-      selectSecurityType() : readSecurityType();
-  }
-
-  //
-  // Read security type from the server (protocol version 3.3).
-  //
-
-  int readSecurityType() throws Exception {
-    int secType = readU32();
-
-    switch (secType) {
-    case SecTypeInvalid:
-      readConnFailedReason();
-      return SecTypeInvalid;	// should never be executed
-    case SecTypeNone:
-    case SecTypeVncAuth:
-      return secType;
-    default:
-      throw new Exception("Unknown security type from RFB server: " + secType);
-    }
-  }
-
-  //
-  // Select security type from the server's list (protocol versions 3.7/3.8).
-  //
-
-  int selectSecurityType() throws Exception {
-    int secType = SecTypeInvalid;
-
-    // Read the list of secutiry types.
-    int nSecTypes = readU8();
-    if (nSecTypes == 0) {
-      readConnFailedReason();
-      return SecTypeInvalid;	// should never be executed
-    }
-    byte[] secTypes = new byte[nSecTypes];
-    readFully(secTypes);
-
-    // Find first supported security type.
-    for (int i = 0; i < nSecTypes; i++) {
-      if (secTypes[i] == SecTypeNone || secTypes[i] == SecTypeVncAuth
-	   || secTypes[i] == SecTypeVeNCrypt) {
-	secType = secTypes[i];
-	break;
-      }
-    }
-
-    if (secType == SecTypeInvalid) {
-      throw new Exception("Server did not offer supported security type");
-    } else {
-      os.write(secType);
-    }
-
-    return secType;
-  }
-
-    int authenticateVeNCrypt() throws Exception {
-	int majorVersion = readU8();
-	int minorVersion = readU8();
-	int Version = (majorVersion << 8) | minorVersion;
-	if (Version < 0x0002) {
-	    os.write(0);
-	    os.write(0);
-	    throw new Exception("Server reported an unsupported VeNCrypt version");
-	}
-	os.write(0);
-	os.write(2);
-	if (readU8() != 0)
-	    throw new Exception("Server reported it could not support the VeNCrypt version");
-	int nSecTypes = readU8();
-	int[] secTypes = new int[nSecTypes];
-	for(int i = 0; i < nSecTypes; i++)
-	    secTypes[i] = readU32();
-
-	for(int i = 0; i < nSecTypes; i++)
-	    switch(secTypes[i])
-		{
-		case SecTypeNone:
-		case SecTypeVncAuth:
-		case SecTypePlain:
-		case SecTypeTLSNone:
-		case SecTypeTLSVnc:
-		case SecTypeTLSPlain:
-		case SecTypeX509None:
-		case SecTypeX509Vnc:
-		case SecTypeX509Plain:
-		    writeInt(secTypes[i]);
-		    return secTypes[i];
-		}
-
-	throw new Exception("No valid VeNCrypt sub-type");
-    }
-
-  //
-  // Perform "no authentication".
-  //
-
-  void authenticateNone() throws Exception {
-    if (clientMinor >= 8)
-      readSecurityResult("No authentication");
-  }
-
-  //
-  // Perform standard VNC Authentication.
-  //
-
-  void authenticateVNC(String pw) throws Exception {
-    byte[] challenge = new byte[16];
-    readFully(challenge);
-
-    if (pw.length() > 8)
-      pw = pw.substring(0, 8);	// Truncate to 8 chars
-
-    // Truncate password on the first zero byte.
-    int firstZero = pw.indexOf(0);
-    if (firstZero != -1)
-      pw = pw.substring(0, firstZero);
-
-    byte[] key = {0, 0, 0, 0, 0, 0, 0, 0};
-    System.arraycopy(pw.getBytes(), 0, key, 0, pw.length());
-
-    DesCipher des = new DesCipher(key);
-
-    des.encrypt(challenge, 0, challenge, 0);
-    des.encrypt(challenge, 8, challenge, 8);
-
-    os.write(challenge);
-
-    readSecurityResult("VNC authentication");
-  }
-
-    void authenticateTLS() throws Exception {
-	TLSTunnel tunnel = new TLSTunnel(sock);
-	tunnel.setup (this);
-    }
-
-    void authenticateX509() throws Exception {
-	X509Tunnel tunnel = new X509Tunnel(sock);
-	tunnel.setup (this);
-    }
-
-    void authenticatePlain(String User, String Password) throws Exception {
-      byte[] user=User.getBytes();
-      byte[] password=Password.getBytes();
-      writeInt(user.length);
-      writeInt(password.length);
-      os.write(user);
-      os.write(password);
-
-      readSecurityResult("Plain authentication");
-    }
-
-  //
-  // Read security result.
-  // Throws an exception on authentication failure.
-  //
-
-  void readSecurityResult(String authType) throws Exception {
-    int securityResult = readU32();
-
-    switch (securityResult) {
-    case VncAuthOK:
-      System.out.println(authType + ": success");
-      break;
-    case VncAuthFailed:
-      if (clientMinor >= 8)
-        readConnFailedReason();
-      throw new Exception(authType + ": failed");
-    case VncAuthTooMany:
-      throw new Exception(authType + ": failed, too many tries");
-    default:
-      throw new Exception(authType + ": unknown result " + securityResult);
-    }
-  }
-
-  //
-  // Read the string describing the reason for a connection failure,
-  // and throw an exception.
-  //
-
-  void readConnFailedReason() throws Exception {
-    int reasonLen = readU32();
-    byte[] reason = new byte[reasonLen];
-    readFully(reason);
-    throw new Exception(new String(reason));
-  }
-
-  //
-  // Write a 32-bit integer into the output stream.
-  //
-
-  void writeInt(int value) throws IOException {
-    byte[] b = new byte[4];
-    b[0] = (byte) ((value >> 24) & 0xff);
-    b[1] = (byte) ((value >> 16) & 0xff);
-    b[2] = (byte) ((value >> 8) & 0xff);
-    b[3] = (byte) (value & 0xff);
-    os.write(b);
-  }
-
-  //
-  // Write the client initialisation message
-  //
-
-  void writeClientInit() throws IOException {
-    if (viewer.options.shareDesktop) {
-      os.write(1);
-    } else {
-      os.write(0);
-    }
-    viewer.options.disableShareDesktop();
-  }
-
-
-  //
-  // Read the server initialisation message
-  //
-
-  String desktopName;
-  int framebufferWidth, framebufferHeight;
-  int bitsPerPixel, depth;
-  boolean bigEndian, trueColour;
-  int redMax, greenMax, blueMax, redShift, greenShift, blueShift;
-
-  void readServerInit() throws IOException {
-    framebufferWidth = readU16();
-    framebufferHeight = readU16();
-    bitsPerPixel = readU8();
-    depth = readU8();
-    bigEndian = (readU8() != 0);
-    trueColour = (readU8() != 0);
-    redMax = readU16();
-    greenMax = readU16();
-    blueMax = readU16();
-    redShift = readU8();
-    greenShift = readU8();
-    blueShift = readU8();
-    byte[] pad = new byte[3];
-    readFully(pad);
-    int nameLength = readU32();
-    byte[] name = new byte[nameLength];
-    readFully(name);
-    desktopName = new String(name);
-    inNormalProtocol = true;
-  }
-
-
-  //
-  // Create session file and write initial protocol messages into it.
-  //
-
-  void startSession(String fname) throws IOException {
-    rec = new SessionRecorder(fname);
-    rec.writeHeader();
-    rec.write(versionMsg_3_3.getBytes());
-    rec.writeIntBE(SecTypeNone);
-    rec.writeShortBE(framebufferWidth);
-    rec.writeShortBE(framebufferHeight);
-    byte[] fbsServerInitMsg =	{
-      32, 24, 0, 1, 0,
-      (byte)0xFF, 0, (byte)0xFF, 0, (byte)0xFF,
-      16, 8, 0, 0, 0, 0
-    };
-    rec.write(fbsServerInitMsg);
-    rec.writeIntBE(desktopName.length());
-    rec.write(desktopName.getBytes());
-    numUpdatesInSession = 0;
-
-    // FIXME: If there were e.g. ZRLE updates only, that should not
-    //        affect recording of Zlib and Tight updates. So, actually
-    //        we should maintain separate flags for Zlib, ZRLE and
-    //        Tight, instead of one ``wereZlibUpdates'' variable.
-    //
-
-    zlibWarningShown = false;
-    tightWarningShown = false;
-  }
-
-  //
-  // Close session file.
-  //
-
-  void closeSession() throws IOException {
-    if (rec != null) {
-      rec.close();
-      rec = null;
-    }
-  }
-
-
-  //
-  // Set new framebuffer size
-  //
-
-  void setFramebufferSize(int width, int height) {
-    framebufferWidth = width;
-    framebufferHeight = height;
-  }
-
-
-  //
-  // Read the server message type
-  //
-
-  int readServerMessageType() throws IOException {
-    int msgType = readU8();
-
-    // If the session is being recorded:
-    if (rec != null) {
-      if (msgType == Bell) {	// Save Bell messages in session files.
-	rec.writeByte(msgType);
-	if (numUpdatesInSession > 0)
-	  rec.flush();
-      }
-    }
-
-    return msgType;
-  }
-
-
-  //
-  // Read a FramebufferUpdate message
-  //
-
-  int updateNRects;
-
-  void readFramebufferUpdate() throws IOException {
-    skipBytes(1);
-    updateNRects = readU16();
-
-    // If the session is being recorded:
-    if (rec != null) {
-      rec.writeByte(FramebufferUpdate);
-      rec.writeByte(0);
-      rec.writeShortBE(updateNRects);
-    }
-
-    numUpdatesInSession++;
-  }
-
-  //
-  // Returns true if encoding is not pseudo
-  //
-  // FIXME: Find better way to differ pseudo and real encodings
-  //
-
-  boolean isRealDecoderEncoding(int encoding) {
-    if ((encoding >= 1) && (encoding <= 16)) {
-      return true;
-    }
-    return false;
-  }
-
-  // Read a FramebufferUpdate rectangle header
-
-  int updateRectX, updateRectY, updateRectW, updateRectH, updateRectEncoding;
-
-  void readFramebufferUpdateRectHdr() throws Exception {
-    updateRectX = readU16();
-    updateRectY = readU16();
-    updateRectW = readU16();
-    updateRectH = readU16();
-    updateRectEncoding = readU32();
-
-    if (updateRectEncoding == EncodingZlib ||
-        updateRectEncoding == EncodingZRLE ||
-	updateRectEncoding == EncodingTight)
-      wereZlibUpdates = true;
-
-    // If the session is being recorded:
-    if (rec != null) {
-      if (numUpdatesInSession > 1)
-	rec.flush();		// Flush the output on each rectangle.
-      rec.writeShortBE(updateRectX);
-      rec.writeShortBE(updateRectY);
-      rec.writeShortBE(updateRectW);
-      rec.writeShortBE(updateRectH);
-
-      //
-      // If this is pseudo encoding or CopyRect that write encoding ID
-      // in this place. All real encoding ID will be written to record stream
-      // in decoder classes.
-
-      if (((!isRealDecoderEncoding(updateRectEncoding))) && (rec != null)) {
-        rec.writeIntBE(updateRectEncoding);
-      }
-    }
-
-    if (updateRectEncoding < 0 || updateRectEncoding > MaxNormalEncoding)
-      return;
-
-    if (updateRectX + updateRectW > framebufferWidth ||
-	updateRectY + updateRectH > framebufferHeight) {
-      throw new Exception("Framebuffer update rectangle too large: " +
-			  updateRectW + "x" + updateRectH + " at (" +
-			  updateRectX + "," + updateRectY + ")");
-    }
-  }
-
-  //
-  // Read a ServerCutText message
-  //
-
-  String readServerCutText() throws IOException {
-    skipBytes(3);
-    int len = readU32();
-    byte[] text = new byte[len];
-    readFully(text);
-    return new String(text);
-  }
-
-
-  //
-  // Read an integer in compact representation (1..3 bytes).
-  // Such format is used as a part of the Tight encoding.
-  // Also, this method records data if session recording is active and
-  // the viewer's recordFromBeginning variable is set to true.
-  //
-
-  int readCompactLen() throws IOException {
-    int[] portion = new int[3];
-    portion[0] = readU8();
-    int byteCount = 1;
-    int len = portion[0] & 0x7F;
-    if ((portion[0] & 0x80) != 0) {
-      portion[1] = readU8();
-      byteCount++;
-      len |= (portion[1] & 0x7F) << 7;
-      if ((portion[1] & 0x80) != 0) {
-	portion[2] = readU8();
-	byteCount++;
-	len |= (portion[2] & 0xFF) << 14;
-      }
-    }
-
-    return len;
-  }
-
-
-  //
-  // Write a FramebufferUpdateRequest message
-  //
-
-  void writeFramebufferUpdateRequest(int x, int y, int w, int h,
-				     boolean incremental)
-       throws IOException
-  {
-    byte[] b = new byte[10];
-
-    b[0] = (byte) FramebufferUpdateRequest;
-    b[1] = (byte) (incremental ? 1 : 0);
-    b[2] = (byte) ((x >> 8) & 0xff);
-    b[3] = (byte) (x & 0xff);
-    b[4] = (byte) ((y >> 8) & 0xff);
-    b[5] = (byte) (y & 0xff);
-    b[6] = (byte) ((w >> 8) & 0xff);
-    b[7] = (byte) (w & 0xff);
-    b[8] = (byte) ((h >> 8) & 0xff);
-    b[9] = (byte) (h & 0xff);
-
-    os.write(b);
-  }
-
-
-  //
-  // Write a SetPixelFormat message
-  //
-
-  void writeSetPixelFormat(int bitsPerPixel, int depth, boolean bigEndian,
-			   boolean trueColour,
-			   int redMax, int greenMax, int blueMax,
-			   int redShift, int greenShift, int blueShift)
-       throws IOException
-  {
-    byte[] b = new byte[20];
-
-    b[0]  = (byte) SetPixelFormat;
-    b[4]  = (byte) bitsPerPixel;
-    b[5]  = (byte) depth;
-    b[6]  = (byte) (bigEndian ? 1 : 0);
-    b[7]  = (byte) (trueColour ? 1 : 0);
-    b[8]  = (byte) ((redMax >> 8) & 0xff);
-    b[9]  = (byte) (redMax & 0xff);
-    b[10] = (byte) ((greenMax >> 8) & 0xff);
-    b[11] = (byte) (greenMax & 0xff);
-    b[12] = (byte) ((blueMax >> 8) & 0xff);
-    b[13] = (byte) (blueMax & 0xff);
-    b[14] = (byte) redShift;
-    b[15] = (byte) greenShift;
-    b[16] = (byte) blueShift;
-
-    os.write(b);
-  }
-
-
-  //
-  // Write a FixColourMapEntries message.  The values in the red, green and
-  // blue arrays are from 0 to 65535.
-  //
-
-  void writeFixColourMapEntries(int firstColour, int nColours,
-				int[] red, int[] green, int[] blue)
-       throws IOException
-  {
-    byte[] b = new byte[6 + nColours * 6];
-
-    b[0] = (byte) FixColourMapEntries;
-    b[2] = (byte) ((firstColour >> 8) & 0xff);
-    b[3] = (byte) (firstColour & 0xff);
-    b[4] = (byte) ((nColours >> 8) & 0xff);
-    b[5] = (byte) (nColours & 0xff);
-
-    for (int i = 0; i < nColours; i++) {
-      b[6 + i * 6]     = (byte) ((red[i] >> 8) & 0xff);
-      b[6 + i * 6 + 1] = (byte) (red[i] & 0xff);
-      b[6 + i * 6 + 2] = (byte) ((green[i] >> 8) & 0xff);
-      b[6 + i * 6 + 3] = (byte) (green[i] & 0xff);
-      b[6 + i * 6 + 4] = (byte) ((blue[i] >> 8) & 0xff);
-      b[6 + i * 6 + 5] = (byte) (blue[i] & 0xff);
-    }
-
-    os.write(b);
-  }
-
-
-  //
-  // Write a SetEncodings message
-  //
-
-  void writeSetEncodings(int[] encs, int len) throws IOException {
-    byte[] b = new byte[4 + 4 * len];
-
-    b[0] = (byte) SetEncodings;
-    b[2] = (byte) ((len >> 8) & 0xff);
-    b[3] = (byte) (len & 0xff);
-
-    for (int i = 0; i < len; i++) {
-      b[4 + 4 * i] = (byte) ((encs[i] >> 24) & 0xff);
-      b[5 + 4 * i] = (byte) ((encs[i] >> 16) & 0xff);
-      b[6 + 4 * i] = (byte) ((encs[i] >> 8) & 0xff);
-      b[7 + 4 * i] = (byte) (encs[i] & 0xff);
-    }
-
-    os.write(b);
-  }
-
-
-  //
-  // Write a ClientCutText message
-  //
-
-  void writeClientCutText(String text) throws IOException {
-    byte[] b = new byte[8 + text.length()];
-
-    b[0] = (byte) ClientCutText;
-    b[4] = (byte) ((text.length() >> 24) & 0xff);
-    b[5] = (byte) ((text.length() >> 16) & 0xff);
-    b[6] = (byte) ((text.length() >> 8) & 0xff);
-    b[7] = (byte) (text.length() & 0xff);
-
-    System.arraycopy(text.getBytes(), 0, b, 8, text.length());
-
-    os.write(b);
-  }
-
-
-  //
-  // A buffer for putting pointer and keyboard events before being sent.  This
-  // is to ensure that multiple RFB events generated from a single Java Event
-  // will all be sent in a single network packet.  The maximum possible
-  // length is 4 modifier down events, a single key event followed by 4
-  // modifier up events i.e. 9 key events or 72 bytes.
-  //
-
-  byte[] eventBuf = new byte[72];
-  int eventBufLen;
-
-
-  // Useful shortcuts for modifier masks.
-
-  final static int CTRL_MASK  = InputEvent.CTRL_MASK;
-  final static int SHIFT_MASK = InputEvent.SHIFT_MASK;
-  final static int META_MASK  = InputEvent.META_MASK;
-  final static int ALT_MASK   = InputEvent.ALT_MASK;
-
-
-  //
-  // Write a pointer event message.  We may need to send modifier key events
-  // around it to set the correct modifier state.
-  //
-
-  int pointerMask = 0;
-
-  void writePointerEvent(MouseEvent evt) throws IOException {
-    int modifiers = evt.getModifiers();
-
-    int mask2 = 2;
-    int mask3 = 4;
-    if (viewer.options.reverseMouseButtons2And3) {
-      mask2 = 4;
-      mask3 = 2;
-    }
-
-    // Note: For some reason, AWT does not set BUTTON1_MASK on left
-    // button presses. Here we think that it was the left button if
-    // modifiers do not include BUTTON2_MASK or BUTTON3_MASK.
-
-    if (evt.getID() == MouseEvent.MOUSE_PRESSED) {
-      if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
-        pointerMask = mask2;
-        modifiers &= ~ALT_MASK;
-      } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) {
-        pointerMask = mask3;
-        modifiers &= ~META_MASK;
-      } else {
-        pointerMask = 1;
-      }
-    } else if (evt.getID() == MouseEvent.MOUSE_RELEASED) {
-      pointerMask = 0;
-      if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
-        modifiers &= ~ALT_MASK;
-      } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) {
-        modifiers &= ~META_MASK;
-      }
-    }
-
-    eventBufLen = 0;
-    writeModifierKeyEvents(modifiers);
-
-    int x = evt.getX();
-    int y = evt.getY();
-
-    if (x < 0) x = 0;
-    if (y < 0) y = 0;
-
-    eventBuf[eventBufLen++] = (byte) PointerEvent;
-    eventBuf[eventBufLen++] = (byte) pointerMask;
-    eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff);
-    eventBuf[eventBufLen++] = (byte) (x & 0xff);
-    eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff);
-    eventBuf[eventBufLen++] = (byte) (y & 0xff);
-
-    //
-    // Always release all modifiers after an "up" event
-    //
-
-    if (pointerMask == 0) {
-      writeModifierKeyEvents(0);
-    }
-
-    os.write(eventBuf, 0, eventBufLen);
-  }
-
-
-  //
-  // Write a key event message.  We may need to send modifier key events
-  // around it to set the correct modifier state.  Also we need to translate
-  // from the Java key values to the X keysym values used by the RFB protocol.
-  //
-
-  void writeKeyEvent(KeyEvent evt) throws IOException {
-
-    int keyChar = evt.getKeyChar();
-
-    //
-    // Ignore event if only modifiers were pressed.
-    //
-
-    // Some JVMs return 0 instead of CHAR_UNDEFINED in getKeyChar().
-    if (keyChar == 0)
-      keyChar = KeyEvent.CHAR_UNDEFINED;
-
-    if (keyChar == KeyEvent.CHAR_UNDEFINED) {
-      int code = evt.getKeyCode();
-      if (code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_SHIFT ||
-          code == KeyEvent.VK_META || code == KeyEvent.VK_ALT)
-        return;
-    }
-
-    //
-    // Key press or key release?
-    //
-
-    boolean down = (evt.getID() == KeyEvent.KEY_PRESSED);
-
-    int key;
-    if (evt.isActionKey()) {
-
-      //
-      // An action key should be one of the following.
-      // If not then just ignore the event.
-      //
-
-      switch(evt.getKeyCode()) {
-      case KeyEvent.VK_HOME:      key = 0xff50; break;
-      case KeyEvent.VK_LEFT:      key = 0xff51; break;
-      case KeyEvent.VK_UP:        key = 0xff52; break;
-      case KeyEvent.VK_RIGHT:     key = 0xff53; break;
-      case KeyEvent.VK_DOWN:      key = 0xff54; break;
-      case KeyEvent.VK_PAGE_UP:   key = 0xff55; break;
-      case KeyEvent.VK_PAGE_DOWN: key = 0xff56; break;
-      case KeyEvent.VK_END:       key = 0xff57; break;
-      case KeyEvent.VK_INSERT:    key = 0xff63; break;
-      case KeyEvent.VK_F1:        key = 0xffbe; break;
-      case KeyEvent.VK_F2:        key = 0xffbf; break;
-      case KeyEvent.VK_F3:        key = 0xffc0; break;
-      case KeyEvent.VK_F4:        key = 0xffc1; break;
-      case KeyEvent.VK_F5:        key = 0xffc2; break;
-      case KeyEvent.VK_F6:        key = 0xffc3; break;
-      case KeyEvent.VK_F7:        key = 0xffc4; break;
-      case KeyEvent.VK_F8:        key = 0xffc5; break;
-      case KeyEvent.VK_F9:        key = 0xffc6; break;
-      case KeyEvent.VK_F10:       key = 0xffc7; break;
-      case KeyEvent.VK_F11:       key = 0xffc8; break;
-      case KeyEvent.VK_F12:       key = 0xffc9; break;
-      default:
-        return;
-      }
-
-    } else {
-
-      //
-      // A "normal" key press.  Ordinary ASCII characters go straight through.
-      // For CTRL-<letter>, CTRL is sent separately so just send <letter>.
-      // Backspace, tab, return, escape and delete have special keysyms.
-      // Anything else we ignore.
-      //
-
-      key = keyChar;
-
-      if (key < 0x20) {
-        if (evt.isControlDown()) {
-          key += 0x60;
-        } else {
-          switch(key) {
-          case KeyEvent.VK_BACK_SPACE: key = 0xff08; break;
-          case KeyEvent.VK_TAB:        key = 0xff09; break;
-          case KeyEvent.VK_ENTER:      key = 0xff0d; break;
-          case KeyEvent.VK_ESCAPE:     key = 0xff1b; break;
-          }
-        }
-      } else if (key == 0x7f) {
-	// Delete
-	key = 0xffff;
-      } else if (key > 0xff) {
-	// JDK1.1 on X incorrectly passes some keysyms straight through,
-	// so we do too.  JDK1.1.4 seems to have fixed this.
-	// The keysyms passed are 0xff00 .. XK_BackSpace .. XK_Delete
-	// Also, we pass through foreign currency keysyms (0x20a0..0x20af).
-	if ((key < 0xff00 || key > 0xffff) &&
-	    !(key >= 0x20a0 && key <= 0x20af))
-	  return;
-      }
-    }
-
-    // Fake keyPresses for keys that only generates keyRelease events
-    if ((key == 0xe5) || (key == 0xc5) || // XK_aring / XK_Aring
-	(key == 0xe4) || (key == 0xc4) || // XK_adiaeresis / XK_Adiaeresis
-	(key == 0xf6) || (key == 0xd6) || // XK_odiaeresis / XK_Odiaeresis
-	(key == 0xa7) || (key == 0xbd) || // XK_section / XK_onehalf
-	(key == 0xa3)) {                  // XK_sterling
-      // Make sure we do not send keypress events twice on platforms
-      // with correct JVMs (those that actually report KeyPress for all
-      // keys)
-      if (down)
-	brokenKeyPressed = true;
-
-      if (!down && !brokenKeyPressed) {
-	// We've got a release event for this key, but haven't received
-        // a press. Fake it.
-	eventBufLen = 0;
-	writeModifierKeyEvents(evt.getModifiers());
-	writeKeyEvent(key, true);
-	os.write(eventBuf, 0, eventBufLen);
-      }
-
-      if (!down)
-	brokenKeyPressed = false;
-    }
-
-    eventBufLen = 0;
-    writeModifierKeyEvents(evt.getModifiers());
-    writeKeyEvent(key, down);
-
-    // Always release all modifiers after an "up" event
-    if (!down)
-      writeModifierKeyEvents(0);
-
-    os.write(eventBuf, 0, eventBufLen);
-  }
-
-
-  //
-  // Add a raw key event with the given X keysym to eventBuf.
-  //
-
-  void writeKeyEvent(int keysym, boolean down) {
-    eventBuf[eventBufLen++] = (byte) KeyboardEvent;
-    eventBuf[eventBufLen++] = (byte) (down ? 1 : 0);
-    eventBuf[eventBufLen++] = (byte) 0;
-    eventBuf[eventBufLen++] = (byte) 0;
-    eventBuf[eventBufLen++] = (byte) ((keysym >> 24) & 0xff);
-    eventBuf[eventBufLen++] = (byte) ((keysym >> 16) & 0xff);
-    eventBuf[eventBufLen++] = (byte) ((keysym >> 8) & 0xff);
-    eventBuf[eventBufLen++] = (byte) (keysym & 0xff);
-  }
-
-
-  //
-  // Write key events to set the correct modifier state.
-  //
-
-  int oldModifiers = 0;
-
-  void writeModifierKeyEvents(int newModifiers) {
-    if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK))
-      writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0);
-
-    if ((newModifiers & SHIFT_MASK) != (oldModifiers & SHIFT_MASK))
-      writeKeyEvent(0xffe1, (newModifiers & SHIFT_MASK) != 0);
-
-    if ((newModifiers & META_MASK) != (oldModifiers & META_MASK))
-      writeKeyEvent(0xffe7, (newModifiers & META_MASK) != 0);
-
-    if ((newModifiers & ALT_MASK) != (oldModifiers & ALT_MASK))
-      writeKeyEvent(0xffe9, (newModifiers & ALT_MASK) != 0);
-
-    oldModifiers = newModifiers;
-  }
-
-
-  public void startTiming() {
-    timing = true;
-
-    // Carry over up to 1s worth of previous rate for smoothing.
-
-    if (timeWaitedIn100us > 10000) {
-      timedKbits = timedKbits * 10000 / timeWaitedIn100us;
-      timeWaitedIn100us = 10000;
-    }
-  }
-
-  public void stopTiming() {
-    timing = false;
-    if (timeWaitedIn100us < timedKbits/2)
-      timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s
-  }
-
-  public long kbitsPerSecond() {
-    return timedKbits * 10000 / timeWaitedIn100us;
-  }
-
-  public long timeWaited() {
-    return timeWaitedIn100us;
-  }
-
-  //
-  // Methods for reading data via our DataInputStream member variable (is).
-  //
-  // In addition to reading data, the readFully() methods updates variables
-  // used to estimate data throughput.
-  //
-
-  public void readFully(byte b[]) throws IOException {
-    readFully(b, 0, b.length);
-  }
-
-  public void readFully(byte b[], int off, int len) throws IOException {
-    long before = 0;
-    if (timing)
-      before = System.currentTimeMillis();
-
-    is.readFully(b, off, len);
-
-    if (timing) {
-      long after = System.currentTimeMillis();
-      long newTimeWaited = (after - before) * 10;
-      int newKbits = len * 8 / 1000;
-
-      // limit rate to between 10kbit/s and 40Mbit/s
-
-      if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000;
-      if (newTimeWaited < newKbits/4)    newTimeWaited = newKbits/4;
-
-      timeWaitedIn100us += newTimeWaited;
-      timedKbits += newKbits;
-    }
-
-    numBytesRead += len;
-  }
-
-  final int available() throws IOException {
-    return is.available();
-  }
-
-  // FIXME: DataInputStream::skipBytes() is not guaranteed to skip
-  //        exactly n bytes. Probably we don't want to use this method.
-  final int skipBytes(int n) throws IOException {
-    int r = is.skipBytes(n);
-    numBytesRead += r;
-    return r;
-  }
-
-  final int readU8() throws IOException {
-    int r = is.readUnsignedByte();
-    numBytesRead++;
-    return r;
-  }
-
-  final int readU16() throws IOException {
-    int r = is.readUnsignedShort();
-    numBytesRead += 2;
-    return r;
-  }
-
-  final int readU32() throws IOException {
-    int r = is.readInt();
-    numBytesRead += 4;
-    return r;
-  }
-
-  public void setStreams(InputStream is_, OutputStream os_) {
-    is = new DataInputStream(is_);
-    os = os_;
-  }
-}
diff --git a/java/src/com/tigervnc/vncviewer/ServerDialog.java b/java/src/com/tigervnc/vncviewer/ServerDialog.java
new file mode 100644
index 0000000..86160f8
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/ServerDialog.java
@@ -0,0 +1,178 @@
+/* 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.vncviewer;
+

+import java.awt.*;

+import java.awt.image.*;

+import java.awt.event.*;

+import javax.swing.*;

+import javax.swing.border.*;

+import java.net.URL;

+import java.io.File;

+import java.util.*;

+
+import com.tigervnc.rfb.*;
+import com.tigervnc.rfb.Exception;
+

+class ServerDialog extends Dialog implements

+                           ActionListener,

+                           ItemListener

+{

+

+  public ServerDialog(OptionsDialog options_,

+                      String defaultServerName, CConn cc_) {

+    

+    super(true);

+    cc = cc_;

+    setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);

+    setResizable(false);

+    setSize(new Dimension(340, 135));

+    setTitle("VNC Viewer : Connection Details");

+

+    options = options_;

+    getContentPane().setLayout(new GridBagLayout());

+

+    JLabel serverLabel = new JLabel("Server:", JLabel.RIGHT);

+    if (options.defaults.getString("server") != null) {

+      server = new JComboBox(options.defaults.getString("server").split(","));

+    } else {

+      server = new JComboBox();

+    }

+

+    // Hack to set the left inset on editable JComboBox

+    if (UIManager.getLookAndFeel().getID() == "Windows") {

+      server.setBorder(BorderFactory.createCompoundBorder(server.getBorder(),

+        BorderFactory.createEmptyBorder(0,2,0,0)));

+    } else {

+      ComboBoxEditor editor = server.getEditor();

+      JTextField jtf = (JTextField)editor.getEditorComponent();

+      jtf.setBorder(new CompoundBorder(jtf.getBorder(), new EmptyBorder(0,2,0,0)));

+    }

+

+    server.setEditable(true);

+    editor = server.getEditor();

+    JLabel encryptionLabel = new JLabel("Encryption:");

+    encryption = new JComboBox();

+    serverLabel.setPreferredSize(encryptionLabel.getPreferredSize());

+

+    JPanel topPanel = new JPanel(new GridBagLayout());

+

+    addGBComponent(new JLabel(cc.logo),topPanel, 0, 0, 1, 1, 0, 0, 0, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(5,5,5,15));

+    addGBComponent(serverLabel,topPanel, 1, 0, 1, 1, 0, 0, 0, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_END, new Insets(10,0,5,5));

+    addGBComponent(server,topPanel, 2, 0, 1, 1, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(10,0,5,40));

+

+    optionsButton = new JButton("Options...");

+    aboutButton = new JButton("About...");

+    okButton = new JButton("OK");

+    cancelButton = new JButton("Cancel");

+    JPanel buttonPanel = new JPanel(new GridBagLayout());

+    buttonPanel.setPreferredSize(new Dimension(340, 40));

+    addGBComponent(aboutButton,buttonPanel, 0, 3, 1, 1, 0, 0, 0.2, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(0,5,0,5));

+    addGBComponent(optionsButton,buttonPanel, 1, 3, 1, 1, 0, 0, 0, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(0,5,0,5));

+    addGBComponent(okButton,buttonPanel, 2, 3, 1, 1, 0, 0, 0.8, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(0,5,0,5));

+    addGBComponent(cancelButton,buttonPanel, 3, 3, 1, 1, 0, 0, 0.5, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(0,5,0,5));

+

+    GridBagConstraints gbc = new GridBagConstraints();

+    gbc.anchor = GridBagConstraints.LINE_START;

+    gbc.fill = GridBagConstraints.BOTH;

+    gbc.gridwidth = GridBagConstraints.REMAINDER;

+    gbc.gridheight = 1;

+    gbc.insets = new Insets(0,0,0,0);

+    gbc.ipadx = 0;

+    gbc.ipady = 0;

+    gbc.weightx = 1;

+    gbc.weighty = 1;

+    getContentPane().add(topPanel,gbc);

+    getContentPane().add(buttonPanel);

+

+    server.addActionListener(this);

+    optionsButton.addActionListener(this);

+    aboutButton.addActionListener(this);

+    okButton.addActionListener(this);

+    cancelButton.addActionListener(this);

+    

+    pack();

+  }

+

+  public void itemStateChanged(ItemEvent e) {

+    Object s = e.getSource();

+    if (s instanceof JComboBox && (JComboBox)s == encryption) {

+      options.encryption=(encryption.getSelectedIndex()==1) ? false : true;

+    }

+  }

+

+  public void actionPerformed(ActionEvent e) {

+    Object s = e.getSource();

+    if (s instanceof JButton && (JButton)s == okButton) {

+      ok = true;

+      endDialog();

+    } else if (s instanceof JButton && (JButton)s == cancelButton) {

+      ok = false;

+      endDialog();

+    } else if (s instanceof JButton && (JButton)s == optionsButton) {

+      options.showDialog();

+    } else if (s instanceof JButton && (JButton)s == aboutButton) {

+      cc.showAbout();

+    } else if (s instanceof JComboBox && (JComboBox)s == server) {

+      if (e.getActionCommand().equals("comboBoxEdited")) {

+        server.insertItemAt(editor.getItem(), 0);

+        server.setSelectedIndex(0);

+        ok = true;

+        endDialog();

+      }

+    }

+  }

+  

+  public void endDialog() {

+    if (ok) {

+      options.defaults.setPref("encryption",(encryption.getSelectedIndex()==1) ? "off" : "on");

+      if (!server.getSelectedItem().toString().equals("")) {

+        String t = (options.defaults.getString("server")==null) ? "" : options.defaults.getString("server");

+        StringTokenizer st = new StringTokenizer(t, ",");

+        StringBuffer sb = new StringBuffer().append((String)server.getSelectedItem());

+        while (st.hasMoreTokens()) {

+          String s = st.nextToken();

+          if (!s.equals((String)server.getSelectedItem()) && !s.equals("")) {

+            sb.append(',');

+            sb.append(s);

+          }

+        }

+        options.defaults.setPref("server", sb.toString());

+      }

+      try {

+        options.defaults.Save();

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

+    }

+    done = true;

+    if (modal) {

+      synchronized (this) {

+        notify();

+      }

+    }

+    this.dispose();

+  }

+

+  CConn cc;

+  JComboBox encryption, server;

+  ComboBoxEditor editor;

+  JButton aboutButton, optionsButton, okButton, cancelButton;

+  OptionsDialog options;

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

+

+}

diff --git a/java/src/com/tigervnc/vncviewer/SessionRecorder.java b/java/src/com/tigervnc/vncviewer/SessionRecorder.java
deleted file mode 100644
index 2ae7079..0000000
--- a/java/src/com/tigervnc/vncviewer/SessionRecorder.java
+++ /dev/null
@@ -1,195 +0,0 @@
-//
-//  Copyright (C) 2002 Constantin Kaplinsky.  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.
-//
-
-//
-// SessionRecorder is a class to write FBS (FrameBuffer Stream) files.
-// FBS files are used to save RFB sessions for later playback.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.io.*;
-
-class SessionRecorder {
-
-  protected FileOutputStream f;
-  protected DataOutputStream df;
-  protected long startTime, lastTimeOffset;
-
-  protected byte[] buffer;
-  protected int bufferSize;
-  protected int bufferBytes;
-
-  public SessionRecorder(String name, int bufsize) throws IOException {
-    f = new FileOutputStream(name);
-    df = new DataOutputStream(f);
-    startTime = System.currentTimeMillis();
-    lastTimeOffset = 0;
-    
-    bufferSize = bufsize;
-    bufferBytes = 0;
-    buffer = new byte[bufferSize];
-  }
-
-  public SessionRecorder(String name) throws IOException {
-    this(name, 65536);
-  }
-
-  //
-  // Close the file, free resources.
-  //
-
-  public void close() throws IOException {
-    try {
-      flush();
-    } catch (IOException e) {
-    }
-
-    df = null;
-    f.close();
-    f = null;
-    buffer = null;
-  }
-
-  //
-  // Write the FBS file header as defined in the rfbproxy utility.
-  //
-
-  public void writeHeader() throws IOException {
-    df.write("FBS 001.000\n".getBytes());
-  }
-
-  //
-  // Write one byte.
-  //
-
-  public void writeByte(int b) throws IOException {
-    prepareWriting();
-    buffer[bufferBytes++] = (byte)b;
-  }
-
-  //
-  // Write 16-bit value, big-endian.
-  //
-
-  public void writeShortBE(int v) throws IOException {
-    prepareWriting();
-    buffer[bufferBytes++] = (byte)(v >> 8);
-    buffer[bufferBytes++] = (byte)v;
-  }
-
-  //
-  // Write 32-bit value, big-endian.
-  //
-
-  public void writeIntBE(int v) throws IOException {
-    prepareWriting();
-    buffer[bufferBytes]     = (byte)(v >> 24);
-    buffer[bufferBytes + 1] = (byte)(v >> 16);
-    buffer[bufferBytes + 2] = (byte)(v >> 8);
-    buffer[bufferBytes + 3] = (byte)v;
-    bufferBytes += 4;
-  }
-
-  //
-  // Write 16-bit value, little-endian.
-  //
-
-  public void writeShortLE(int v) throws IOException {
-    prepareWriting();
-    buffer[bufferBytes++] = (byte)v;
-    buffer[bufferBytes++] = (byte)(v >> 8);
-  }
-
-  //
-  // Write 32-bit value, little-endian.
-  //
-
-  public void writeIntLE(int v) throws IOException {
-    prepareWriting();
-    buffer[bufferBytes]     = (byte)v;
-    buffer[bufferBytes + 1] = (byte)(v >> 8);
-    buffer[bufferBytes + 2] = (byte)(v >> 16);
-    buffer[bufferBytes + 3] = (byte)(v >> 24);
-    bufferBytes += 4;
-  }
-
-  //
-  // Write byte arrays.
-  //
-
-  public void write(byte b[], int off, int len) throws IOException {
-    prepareWriting();
-    while (len > 0) {
-      if (bufferBytes > bufferSize - 4)
-	flush(false);
-
-      int partLen;
-      if (bufferBytes + len > bufferSize) {
-	partLen = bufferSize - bufferBytes;
-      } else {
-	partLen = len;
-      }
-      System.arraycopy(b, off, buffer, bufferBytes, partLen);
-      bufferBytes += partLen;
-      off += partLen;
-      len -= partLen;
-    }
-  }
-
-  public void write(byte b[]) throws IOException {
-    write(b, 0, b.length);
-  }
-
-  //
-  // Flush the output. This method saves buffered data in the
-  // underlying file object adding data sizes and timestamps. If the
-  // updateTimeOffset is set to false, then the current time offset
-  // will not be changed for next write operation.
-  //
-
-  public void flush(boolean updateTimeOffset) throws IOException {
-    if (bufferBytes > 0) {
-      df.writeInt(bufferBytes);
-      df.write(buffer, 0, (bufferBytes + 3) & 0x7FFFFFFC);
-      df.writeInt((int)lastTimeOffset);
-      bufferBytes = 0;
-      if (updateTimeOffset)
-	lastTimeOffset = -1;
-    }
-  }
-
-  public void flush() throws IOException {
-    flush(true);
-  }
-
-  //
-  // Before writing any data, remember time offset and flush the
-  // buffer before it becomes full.
-  //
-
-  protected void prepareWriting() throws IOException {
-    if (lastTimeOffset == -1)
-      lastTimeOffset = System.currentTimeMillis() - startTime;
-    if (bufferBytes > bufferSize - 4)
-      flush(false);
-  }
-
-}
-
diff --git a/java/src/com/tigervnc/vncviewer/SocketFactory.java b/java/src/com/tigervnc/vncviewer/SocketFactory.java
deleted file mode 100644
index 30460dc..0000000
--- a/java/src/com/tigervnc/vncviewer/SocketFactory.java
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-//  Copyright (C) 2002 HorizonLive.com, Inc.  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.
-//
-
-//
-// SocketFactory.java describes an interface used to substitute the
-// standard Socket class by its alternative implementations.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.applet.*;
-import java.net.*;
-import java.io.*;
-
-public interface SocketFactory {
-
-  public Socket createSocket(String host, int port, Applet applet)
-    throws IOException;
-
-  public Socket createSocket(String host, int port, String[] args)
-    throws IOException;
-}
diff --git a/java/src/com/tigervnc/vncviewer/TLSTunnel.java b/java/src/com/tigervnc/vncviewer/TLSTunnel.java
deleted file mode 100644
index 00cfb4a..0000000
--- a/java/src/com/tigervnc/vncviewer/TLSTunnel.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2003 Sun Microsystems, Inc.
- * Copyright (C) 2003-2010 Martin Koegler
- * Copyright (C) 2006 OCCAM Financial Technology
- *
- * 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.vncviewer;
-
-import java.util.*;
-import java.net.*;
-import javax.net.ssl.*;
-
-public class TLSTunnel extends TLSTunnelBase
-{
-
-  public TLSTunnel (Socket sock_)
-  {
-    super (sock_);
-  }
-
-
-  protected void setParam (SSLSocket sock)
-  {
-    String[]supported;
-    ArrayList enabled = new ArrayList ();
-
-    supported = sock.getSupportedCipherSuites ();
-
-    for (int i = 0; i < supported.length; i++)
-      if (supported[i].matches (".*DH_anon.*"))
-	enabled.add (supported[i]);
-
-    sock.setEnabledCipherSuites ((String[])enabled.toArray (new String[0]));
-  }
-
-}
diff --git a/java/src/com/tigervnc/vncviewer/TLSTunnelBase.java b/java/src/com/tigervnc/vncviewer/TLSTunnelBase.java
deleted file mode 100644
index 922e837..0000000
--- a/java/src/com/tigervnc/vncviewer/TLSTunnelBase.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2003 Sun Microsystems, Inc.
- * Copyright (C) 2003-2010 Martin Koegler
- *
- * 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.vncviewer;
-
-import java.util.ArrayList;
-import java.net.*;
-import javax.net.ssl.*;
-
-public abstract class TLSTunnelBase
-{
-
-  public TLSTunnelBase (Socket sock_)
-  {
-    sock = sock_;
-  }
-
-  protected void initContext (SSLContext sc) throws java.security.
-    GeneralSecurityException
-  {
-    sc.init (null, null, null);
-  }
-
-  public void setup (RfbProto cc) throws Exception
-  {
-    if (cc.readU8 () == 0)
-	throw new Exception("Setup on the server failed");
-    try
-    {
-      SSLSocketFactory sslfactory;
-      SSLSocket sslsock;
-      SSLContext sc = SSLContext.getInstance ("TLS");
-      System.out.println("Generating TLS context");
-      initContext (sc);
-      System.out.println("Doing TLS handshake");
-      sslfactory = sc.getSocketFactory ();
-      sslsock = (SSLSocket) sslfactory.createSocket (sock,
-						     sock.getInetAddress ().
-						     getHostName (),
-						     sock.getPort (), true);
-
-      setParam (sslsock);
-
-      /* Not neccessary - just ensures that we know what cipher
-       * suite we are using for the output of toString()
-       */
-      sslsock.startHandshake ();
-
-      System.out.println("TLS done");
-
-      cc.setStreams (sslsock.getInputStream (),
-		     sslsock.getOutputStream ());
-    }
-    catch (java.io.IOException e)
-    {
-      throw new Exception("TLS handshake failed " + e.toString ());
-    }
-    catch (java.security.GeneralSecurityException e)
-    {
-      throw new Exception("TLS handshake failed " + e.toString ());
-    }
-  }
-
-
-  protected abstract void setParam (SSLSocket sock);
-
-  Socket sock;
-
-}
diff --git a/java/src/com/tigervnc/vncviewer/UserPrefs.java b/java/src/com/tigervnc/vncviewer/UserPrefs.java
new file mode 100644
index 0000000..51b8b90
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/UserPrefs.java
@@ -0,0 +1,258 @@
+/* Copyright (C) 2011 TigerVNC Team.  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.vncviewer;
+
+import java.util.Properties;
+import java.io.FileOutputStream;
+import javax.swing.filechooser.*;
+
+/**
+ * Simple, generic class to load up or save properties for an application.
+ *  (eg user preferences)
+ *
+ * While reading in values, it will automatically convert from string format,
+ * to your choice of int or boolean, if desired.
+ *
+ * We assume that you want properties stored in a file named
+ * $HOME/.yourappname, where $HOME represents the users "home directory"
+ *
+ * Feel free to email comments or suggestions to the author
+ *
+ * @version 1.0, 09/16/1997
+ * @author Philip Brown phil@bolthole.com
+ */
+
+public class UserPrefs extends Properties {
+	String userhome=null; //This will have fileseperator on end if it
+	String prefFile;
+	String appName;
+	
+	Properties systemprops;
+	
+
+	/**
+	 * We try to read in a preferences file, from the user's "HOME"
+	 * directory. We base the name of the file, on the name of the
+	 * application we are in.
+	 * Use the getHomeDir() call if you want to know what directory
+	 * this is in.
+	 *
+	 * @param appName_ name of application calling this class
+	 */
+	public UserPrefs(String appName_) {
+		appName = appName_;
+		
+		systemprops=System.getProperties();
+		// This is guaranteed as always being some valid directory,
+		// according to spec.
+		prefFile= getHomeDir()+getFileSeperator()+
+                "."+appName;
+		try {
+			load(new java.io.FileInputStream(prefFile));
+		} catch (Exception err)	{
+			if(err instanceof java.io.FileNotFoundException) {
+        try {
+		      store(new FileOutputStream(prefFile), appName+" preferences");
+        } catch (Exception e) { /* FIXME */ }
+      } else {
+        // FIXME - should be a dialog
+			  System.out.println("Error opening prefs file:"+err.getMessage());
+      }
+		}
+		
+	}
+
+	// Strip off any comments
+	String trimValue(String prop) {
+		if(prop==null)
+			return null;
+		
+		int lastpos;
+		lastpos=prop.indexOf('#');
+		if(lastpos==-1)
+			lastpos=prop.length()-1;
+		while((prop.charAt(lastpos)==' ') ||
+		      (prop.charAt(lastpos)=='\t'))	{
+			lastpos--;
+			if(lastpos==0)
+				break;
+		}
+		      
+		return prop.substring(0, lastpos+1);
+	}
+
+	/**
+	 * The java spec guarantees that a "home" directory be
+	 * specified. We look it up for our own uses at initialization
+	 * If you want to do other things in the user's home dir,
+	 * this routine is an easy way to find out where it is.
+	 *
+	 *  This returns string that will have trailing fileseparator, eg "/")
+	 *  so you can slap together a filename directly after it, and
+	 *  not worry about that sort of junk.
+	 */
+  final public static String getHomeDir() {
+    String homeDir = null;
+    String os = System.getProperty("os.name");
+    try {
+      if (os.startsWith("Windows")) {
+        // JRE prior to 1.5 cannot reliably determine USERPROFILE
+        // return user.home and hope it's right...
+        if (Integer.parseInt(System.getProperty("java.version").split("\\.")[1]) < 5) {
+          homeDir = System.getProperty("user.home");
+        } else {
+          homeDir = System.getenv("USERPROFILE");
+        }
+      } else {
+        homeDir = FileSystemView.getFileSystemView().
+          getDefaultDirectory().getCanonicalPath();
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    return homeDir;
+  }
+
+  final private String getUserName() {
+    String userName = (String) System.getProperties().get("user.name");
+    return userName;
+  }
+
+  final public static String getFileSeperator() {
+    String seperator = System.getProperties().get("file.separator").toString();
+    return seperator;
+  }
+
+	/**
+	 * way to directly set a preference. You'll have to
+	 * do your own type conversion.
+	 * @param prefname name of property
+	 * @param value string value of property
+	 */
+	public void setPref(String prefname, String value) {
+		setProperty(prefname, value);
+	}
+
+	public void setPref(String prefname, int value) {
+		systemprops.put(prefname, java.lang.Integer.toString(value));
+	}
+
+	public void setPref(String prefname, boolean value) {
+		put(prefname, (value ? "true" : "false"));
+	}
+	
+	/**
+	 * Gets named resource, as a string value.
+	 * returns null if no such resource defined.
+	 * @param name name of property
+	 */
+	public String getString(String name) {
+		return trimValue(getProperty(name));
+	}
+	/**
+	 * Gets named resource, as a string value.
+	 *
+	 * @param name name of property
+	 * @param defval default value to remember and return , if
+	 *   no existing property name found.
+	 */
+	public String getString(String name, String defstr) {
+		String val = trimValue(getProperty(name));
+		if(val==null) {
+			setPref(name, defstr);
+			return defstr;
+		}
+		return val;
+	}
+
+	/**
+	 * look up property that is an int value
+	 * @param name name of property
+	 */	
+	public int getInt(String name) {
+		String strint = trimValue(getProperty(name));
+		int val=0;
+		try {
+			val = Integer.parseInt(strint);
+		} catch (NumberFormatException err) {
+			//we dont care
+		}
+		return val;
+	}
+
+	/**
+	 * look up property that is an int value
+	 * @param name name of property
+	 * @param defval default value to remember and return , if
+	 *   no existing property name found.
+	 */	
+	public int getInt(String name, int defval) {
+		String strint = trimValue(getProperty(name));
+		if(strint==null) {
+			setPref(name, String.valueOf(defval));
+			return defval;
+		}
+		int val=0;
+		try {
+			val = Integer.parseInt(strint);
+		} catch (NumberFormatException err) {
+			//we dont care
+		}
+		return val;
+	}
+
+	/**
+	 * look up property that is a boolean value
+	 * @param name name of property
+	 * @param defval default value to remember and return , if
+	 *   no existing property name found.
+	 */	
+	public boolean getBool(String name) {
+		String strval = trimValue(getProperty(name));
+		if(strval.equals("true"))
+			return true;
+
+		return false;
+	}
+
+	/**
+	 * @param name name of property
+	 * @param defval default value to remember and return , if
+	 *   no existing property name found.
+	 */	
+	public boolean getBool(String name, boolean defval) {
+		String strval = trimValue(getProperty(name));
+		if(strval==null) {
+			setPref(name, String.valueOf(defval));
+			return defval;
+		}
+
+		if(strval.equals("true"))
+			return true;
+
+		return false;
+	}
+
+	/**
+	* save user preferences to default file. Duh.
+	*/
+	public void Save() throws java.io.IOException {
+		store(new FileOutputStream(prefFile), appName+" preferences");
+	}
+}
diff --git a/java/src/com/tigervnc/vncviewer/VncCanvas.java b/java/src/com/tigervnc/vncviewer/VncCanvas.java
deleted file mode 100644
index b776481..0000000
--- a/java/src/com/tigervnc/vncviewer/VncCanvas.java
+++ /dev/null
@@ -1,1092 +0,0 @@
-//
-//  Copyright (C) 2004 Horizon Wimba.  All Rights Reserved.
-//  Copyright (C) 2001-2003 HorizonLive.com, Inc.  All Rights Reserved.
-//  Copyright (C) 2001,2002 Constantin Kaplinsky.  All Rights Reserved.
-//  Copyright (C) 2000 Tridia Corporation.  All Rights Reserved.
-//  Copyright (C) 1999 AT&T Laboratories Cambridge.  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.vncviewer;
-
-import com.tigervnc.decoder.CoRREDecoder;
-import com.tigervnc.decoder.CopyRectDecoder;
-import com.tigervnc.decoder.HextileDecoder;
-import com.tigervnc.decoder.RREDecoder;
-import com.tigervnc.decoder.RawDecoder;
-import com.tigervnc.decoder.TightDecoder;
-import com.tigervnc.decoder.ZRLEDecoder;
-import com.tigervnc.decoder.ZlibDecoder;
-import com.tigervnc.decoder.common.Repaintable;
-import java.awt.*;
-import java.awt.event.*;
-import java.awt.image.*;
-import java.io.*;
-import java.lang.*;
-import java.util.zip.*;
-
-
-//
-// VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
-//
-
-class VncCanvas extends Canvas
-  implements KeyListener, MouseListener, MouseMotionListener, Repaintable, Runnable {
-
-  VncViewer viewer;
-  RfbProto rfb;
-  ColorModel cm8, cm24;
-  int bytesPixel;
-
-  int maxWidth = 0, maxHeight = 0;
-  int scalingFactor;
-  int scaledWidth, scaledHeight;
-
-  Image memImage;
-  Graphics memGraphics;
-
-  //
-  // Decoders
-  //
-
-  RawDecoder rawDecoder;
-  RREDecoder rreDecoder;
-  CoRREDecoder correDecoder;
-  ZlibDecoder zlibDecoder;
-  HextileDecoder hextileDecoder;
-  ZRLEDecoder zrleDecoder;
-  TightDecoder tightDecoder;
-  CopyRectDecoder copyRectDecoder;
-
-  // Base decoder decoders array
-  RawDecoder []decoders = null;
-
-  // Update statistics.
-  long statStartTime;           // time on first framebufferUpdateRequest
-  long statNumUpdates;           // counter for FramebufferUpdate messages
-  long statNumTotalRects;        // rectangles in FramebufferUpdate messages
-  long statNumPixelRects;        // the same, but excluding pseudo-rectangles
-  long statNumRectsTight;        // Tight-encoded rectangles (including JPEG)
-  long statNumRectsTightJPEG;    // JPEG-compressed Tight-encoded rectangles
-  long statNumRectsZRLE;         // ZRLE-encoded rectangles
-  long statNumRectsHextile;      // Hextile-encoded rectangles
-  long statNumRectsRaw;          // Raw-encoded rectangles
-  long statNumRectsCopy;         // CopyRect rectangles
-  long statNumBytesEncoded;      // number of bytes in updates, as received
-  long statNumBytesDecoded;      // number of bytes, as if Raw encoding was used
-
-  // True if we process keyboard and mouse events.
-  boolean inputEnabled;
-
-  // True if was no one auto resize of canvas
-  boolean isFirstSizeAutoUpdate = true;
-
-  // Members for limiting sending mouse events to server
-  long lastMouseEventSendTime = System.currentTimeMillis();
-  long mouseMaxFreq = 20;
-
-  //
-  // The constructors.
-  //
-
-  public VncCanvas(VncViewer v, int maxWidth_, int maxHeight_)
-    throws IOException {
-
-    viewer = v;
-    maxWidth = maxWidth_;
-    maxHeight = maxHeight_;
-
-    rfb = viewer.rfb;
-    scalingFactor = viewer.options.scalingFactor;
-
-    cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6));
-    cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
-
-    //
-    // Create decoders
-    //
-
-    // Input stream for decoders
-    RfbInputStream rfbis = new RfbInputStream(rfb);
-    // Create output stream for session recording
-    RecordOutputStream ros = new RecordOutputStream(rfb);
-
-    rawDecoder = new RawDecoder(memGraphics, rfbis);
-    rreDecoder = new RREDecoder(memGraphics, rfbis);
-    correDecoder = new CoRREDecoder(memGraphics, rfbis);
-    hextileDecoder = new HextileDecoder(memGraphics, rfbis);
-    tightDecoder = new TightDecoder(memGraphics, rfbis);
-    zlibDecoder = new ZlibDecoder(memGraphics, rfbis);
-    zrleDecoder = new ZRLEDecoder(memGraphics, rfbis);
-    copyRectDecoder = new CopyRectDecoder(memGraphics, rfbis);
-
-    //
-    // Set data for decoders that needs extra parameters
-    //
-
-    hextileDecoder.setRepainableControl(this);
-    tightDecoder.setRepainableControl(this);
-
-    //
-    // Create array that contains our decoders
-    //
-
-    decoders = new RawDecoder[8];
-    decoders[0] = rawDecoder;
-    decoders[1] = rreDecoder;
-    decoders[2] = correDecoder;
-    decoders[3] = hextileDecoder;
-    decoders[4] = zlibDecoder;
-    decoders[5] = tightDecoder;
-    decoders[6] = zrleDecoder;
-    decoders[7] = copyRectDecoder;
-
-    //
-    // Set session recorder for decoders
-    //
-
-    for (int i = 0; i < decoders.length; i++) {
-      decoders[i].setDataOutputStream(ros);
-    }
-
-    setPixelFormat();
-
-    inputEnabled = false;
-    if (!viewer.options.viewOnly)
-      enableInput(true);
-
-    // Enable mouse and keyboard event listeners.
-    addKeyListener(this);
-    addMouseListener(this);
-    addMouseMotionListener(this);
-
-    // Create thread, that will send mouse movement events
-    // to VNC server.
-    Thread mouseThread = new Thread(this);
-    mouseThread.start();
-  }
-
-  public VncCanvas(VncViewer v) throws IOException {
-    this(v, 0, 0);
-  }
-
-  //
-  // Callback methods to determine geometry of our Component.
-  //
-
-  public Dimension getPreferredSize() {
-    return new Dimension(scaledWidth, scaledHeight);
-  }
-
-  public Dimension getMinimumSize() {
-    return new Dimension(scaledWidth, scaledHeight);
-  }
-
-  public Dimension getMaximumSize() {
-    return new Dimension(scaledWidth, scaledHeight);
-  }
-
-  //
-  // All painting is performed here.
-  //
-
-  public void update(Graphics g) {
-    paint(g);
-  }
-
-  public void paint(Graphics g) {
-    synchronized(memImage) {
-      if (rfb.framebufferWidth == scaledWidth) {
-        g.drawImage(memImage, 0, 0, null);
-      } else {
-        paintScaledFrameBuffer(g);
-      }
-    }
-    if (showSoftCursor) {
-      int x0 = cursorX - hotX, y0 = cursorY - hotY;
-      Rectangle r = new Rectangle(x0, y0, cursorWidth, cursorHeight);
-      if (r.intersects(g.getClipBounds())) {
-	g.drawImage(softCursor, x0, y0, null);
-      }
-    }
-  }
-
-  public void paintScaledFrameBuffer(Graphics g) {
-    g.drawImage(memImage, 0, 0, scaledWidth, scaledHeight, null);
-  }
-
-  //
-  // Start/stop receiving mouse events. Keyboard events are received
-  // even in view-only mode, because we want to map the 'r' key to the
-  // screen refreshing function.
-  //
-
-  public synchronized void enableInput(boolean enable) {
-    if (enable && !inputEnabled) {
-      inputEnabled = true;
-      if (viewer.showControls) {
-	viewer.buttonPanel.enableRemoteAccessControls(true);
-      }
-      createSoftCursor();	// scaled cursor
-    } else if (!enable && inputEnabled) {
-      inputEnabled = false;
-      if (viewer.showControls) {
-	viewer.buttonPanel.enableRemoteAccessControls(false);
-      }
-      createSoftCursor();	// non-scaled cursor
-    }
-  }
-
-  public void setPixelFormat() throws IOException {
-    if (viewer.options.eightBitColors) {
-      rfb.writeSetPixelFormat(8, 8, false, true, 7, 7, 3, 0, 3, 6);
-      bytesPixel = 1;
-    } else {
-      rfb.writeSetPixelFormat(32, 24, false, true, 255, 255, 255, 16, 8, 0);
-      bytesPixel = 4;
-    }
-    updateFramebufferSize();
-  }
-
-  void setScalingFactor(int sf) {
-    scalingFactor = sf;
-    updateFramebufferSize();
-    invalidate();
-  }
-
-  void updateFramebufferSize() {
-
-    // Useful shortcuts.
-    int fbWidth = rfb.framebufferWidth;
-    int fbHeight = rfb.framebufferHeight;
-
-    // FIXME: This part of code must be in VncViewer i think
-    if (viewer.options.autoScale) {
-      if (viewer.inAnApplet) {
-        maxWidth = viewer.getWidth();
-        maxHeight = viewer.getHeight();
-      } else {
-        if (viewer.vncFrame != null) {
-          if (isFirstSizeAutoUpdate) {
-            isFirstSizeAutoUpdate = false;
-            Dimension screenSize = viewer.vncFrame.getToolkit().getScreenSize();
-            maxWidth = (int)screenSize.getWidth() - 100;
-            maxHeight = (int)screenSize.getHeight() - 100;
-            viewer.vncFrame.setSize(maxWidth, maxHeight);
-          } else {
-            viewer.desktopScrollPane.doLayout();
-            maxWidth = viewer.desktopScrollPane.getWidth();
-            maxHeight = viewer.desktopScrollPane.getHeight();
-          }
-        } else {
-          maxWidth = fbWidth;
-          maxHeight = fbHeight;
-        }
-      }
-      int f1 = maxWidth * 100 / fbWidth;
-      int f2 = maxHeight * 100 / fbHeight;
-      scalingFactor = Math.min(f1, f2);
-      if (scalingFactor > 100)
-	scalingFactor = 100;
-      System.out.println("Scaling desktop at " + scalingFactor + "%");
-    }
-
-    // Update scaled framebuffer geometry.
-    scaledWidth = (fbWidth * scalingFactor + 50) / 100;
-    scaledHeight = (fbHeight * scalingFactor + 50) / 100;
-
-    // Create new off-screen image either if it does not exist, or if
-    // its geometry should be changed. It's not necessary to replace
-    // existing image if only pixel format should be changed.
-    if (memImage == null) {
-      memImage = viewer.vncContainer.createImage(fbWidth, fbHeight);
-      memGraphics = memImage.getGraphics();
-    } else if (memImage.getWidth(null) != fbWidth ||
-	       memImage.getHeight(null) != fbHeight) {
-      synchronized(memImage) {
-	memImage = viewer.vncContainer.createImage(fbWidth, fbHeight);
-	memGraphics = memImage.getGraphics();
-      }
-    }
-
-    //
-    // Update decoders
-    //
-
-    //
-    // FIXME: Why decoders can be null here?
-    //
-
-    if (decoders != null) {
-      for (int i = 0; i < decoders.length; i++) {
-        //
-        // Set changes to every decoder that we can use
-        //
-
-        decoders[i].setBPP(bytesPixel);
-        decoders[i].setFrameBufferSize(fbWidth, fbHeight);
-        decoders[i].setGraphics(memGraphics);
-
-        //
-        // Update decoder
-        //
-
-        decoders[i].update();
-      }
-    }
-
-    // FIXME: This part of code must be in VncViewer i think
-    // Update the size of desktop containers.
-    if (viewer.inSeparateFrame) {
-      if (viewer.desktopScrollPane != null) {
-        if (!viewer.options.autoScale) {
-          resizeDesktopFrame();
-        } else {
-          setSize(scaledWidth, scaledHeight);
-          viewer.desktopScrollPane.setSize(maxWidth + 200,
-                                           maxHeight + 200);
-        }
-      }
-    } else {
-      setSize(scaledWidth, scaledHeight);
-    }
-    viewer.moveFocusToDesktop();
-  }
-
-  void resizeDesktopFrame() {
-    setSize(scaledWidth, scaledHeight);
-
-    // FIXME: Find a better way to determine correct size of a
-    // ScrollPane.  -- const
-    Insets insets = viewer.desktopScrollPane.getInsets();
-    viewer.desktopScrollPane.setSize(scaledWidth +
-				     2 * Math.min(insets.left, insets.right),
-				     scaledHeight +
-				     2 * Math.min(insets.top, insets.bottom));
-
-    viewer.vncFrame.pack();
-
-    // Try to limit the frame size to the screen size.
-
-    Dimension screenSize = viewer.vncFrame.getToolkit().getScreenSize();
-    Dimension frameSize = viewer.vncFrame.getSize();
-    Dimension newSize = frameSize;
-
-    // Reduce Screen Size by 30 pixels in each direction;
-    // This is a (poor) attempt to account for
-    //     1) Menu bar on Macintosh (should really also account for
-    //        Dock on OSX).  Usually 22px on top of screen.
-    //     2) Taxkbar on Windows (usually about 28 px on bottom)
-    //     3) Other obstructions.
-
-    screenSize.height -= 30;
-    screenSize.width  -= 30;
-
-    boolean needToResizeFrame = false;
-    if (frameSize.height > screenSize.height) {
-      newSize.height = screenSize.height;
-      needToResizeFrame = true;
-    }
-    if (frameSize.width > screenSize.width) {
-      newSize.width = screenSize.width;
-      needToResizeFrame = true;
-    }
-    if (needToResizeFrame) {
-      viewer.vncFrame.setSize(newSize);
-    }
-
-    viewer.desktopScrollPane.doLayout();
-  }
-
-  //
-  // processNormalProtocol() - executed by the rfbThread to deal with the
-  // RFB socket.
-  //
-
-  public void processNormalProtocol() throws Exception {
-
-    // Start/stop session recording if necessary.
-    viewer.checkRecordingStatus();
-
-    rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth,
-				      rfb.framebufferHeight, false);
-
-    resetStats();
-    boolean statsRestarted = false;
-
-    //
-    // main dispatch loop
-    //
-
-    while (true) {
-
-      // Read message type from the server.
-      int msgType = rfb.readServerMessageType();
-
-      // Process the message depending on its type.
-      switch (msgType) {
-      case RfbProto.FramebufferUpdate:
-
-        if (statNumUpdates == viewer.debugStatsExcludeUpdates &&
-            !statsRestarted) {
-          resetStats();
-          statsRestarted = true;
-        } else if (statNumUpdates == viewer.debugStatsMeasureUpdates &&
-                   statsRestarted) {
-          viewer.disconnect();
-        }
-
-	rfb.readFramebufferUpdate();
-	statNumUpdates++;
-
-	boolean cursorPosReceived = false;
-
-	for (int i = 0; i < rfb.updateNRects; i++) {
-
-	  rfb.readFramebufferUpdateRectHdr();
-	  statNumTotalRects++;
-	  int rx = rfb.updateRectX, ry = rfb.updateRectY;
-	  int rw = rfb.updateRectW, rh = rfb.updateRectH;
-
-	  if (rfb.updateRectEncoding == rfb.EncodingLastRect)
-	    break;
-
-	  if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) {
-	    rfb.setFramebufferSize(rw, rh);
-	    updateFramebufferSize();
-	    break;
-	  }
-
-	  if (rfb.updateRectEncoding == rfb.EncodingXCursor ||
-	      rfb.updateRectEncoding == rfb.EncodingRichCursor) {
-	    handleCursorShapeUpdate(rfb.updateRectEncoding, rx, ry, rw, rh);
-	    continue;
-	  }
-
-	  if (rfb.updateRectEncoding == rfb.EncodingPointerPos) {
-	    softCursorMove(rx, ry);
-	    cursorPosReceived = true;
-	    continue;
-	  }
-
-          long numBytesReadBefore = rfb.getNumBytesRead();
-
-          rfb.startTiming();
-
-	  switch (rfb.updateRectEncoding) {
-	  case RfbProto.EncodingRaw:
-	    statNumRectsRaw++;
-	    handleRawRect(rx, ry, rw, rh);
-	    break;
-	  case RfbProto.EncodingCopyRect:
-	    statNumRectsCopy++;
-	    handleCopyRect(rx, ry, rw, rh);
-	    break;
-	  case RfbProto.EncodingRRE:
-	    handleRRERect(rx, ry, rw, rh);
-	    break;
-	  case RfbProto.EncodingCoRRE:
-	    handleCoRRERect(rx, ry, rw, rh);
-	    break;
-	  case RfbProto.EncodingHextile:
-	    statNumRectsHextile++;
-	    handleHextileRect(rx, ry, rw, rh);
-	    break;
-	  case RfbProto.EncodingZRLE:
-	    statNumRectsZRLE++;
-	    handleZRLERect(rx, ry, rw, rh);
-	    break;
-	  case RfbProto.EncodingZlib:
-            handleZlibRect(rx, ry, rw, rh);
-	    break;
-	  case RfbProto.EncodingTight:
-            if (tightDecoder != null) {
-	      statNumRectsTightJPEG = tightDecoder.getNumJPEGRects();
-              //statNumRectsTight = tightDecoder.getNumTightRects();
-            }
-            statNumRectsTight++;
-	    handleTightRect(rx, ry, rw, rh);
-	    break;
-	  default:
-	    throw new Exception("Unknown RFB rectangle encoding " +
-				rfb.updateRectEncoding);
-	  }
-
-          rfb.stopTiming();
-
-          statNumPixelRects++;
-          statNumBytesDecoded += rw * rh * bytesPixel;
-          statNumBytesEncoded +=
-            (int)(rfb.getNumBytesRead() - numBytesReadBefore);
-	}
-
-	boolean fullUpdateNeeded = false;
-
-	// Start/stop session recording if necessary. Request full
-	// update if a new session file was opened.
-	if (viewer.checkRecordingStatus())
-	  fullUpdateNeeded = true;
-
-	// Defer framebuffer update request if necessary. But wake up
-	// immediately on keyboard or mouse event. Also, don't sleep
-	// if there is some data to receive, or if the last update
-	// included a PointerPos message.
-	if (viewer.deferUpdateRequests > 0 &&
-	    rfb.available() == 0 && !cursorPosReceived) {
-	  synchronized(rfb) {
-	    try {
-	      rfb.wait(viewer.deferUpdateRequests);
-	    } catch (InterruptedException e) {
-	    }
-	  }
-	}
-
-        viewer.autoSelectEncodings();
-
-	// Before requesting framebuffer update, check if the pixel
-	// format should be changed.
-	if (viewer.options.eightBitColors != (bytesPixel == 1)) {
-          // Pixel format should be changed.
-          setPixelFormat();
-          fullUpdateNeeded = true;
-	}
-
-        // Finally, request framebuffer update if needed.
-        if (fullUpdateNeeded) {
-          rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth,
-                                            rfb.framebufferHeight, false);
-        } else {
-          rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth,
-                                            rfb.framebufferHeight, true);
-        }
-
-	break;
-
-      case RfbProto.SetColourMapEntries:
-	throw new Exception("Can't handle SetColourMapEntries message");
-
-      case RfbProto.Bell:
-        Toolkit.getDefaultToolkit().beep();
-	break;
-
-      case RfbProto.ServerCutText:
-	String s = rfb.readServerCutText();
-	viewer.clipboard.setCutText(s);
-	break;
-
-      default:
-	throw new Exception("Unknown RFB message type " + msgType);
-      }
-    }
-  }
-
-  //
-  // Handle a raw rectangle. The second form with paint==false is used
-  // by the Hextile decoder for raw-encoded tiles.
-  //
-
-  void handleRawRect(int x, int y, int w, int h) throws IOException, Exception {
-    handleRawRect(x, y, w, h, true);
-  }
-
-  void handleRawRect(int x, int y, int w, int h, boolean paint)
-    throws IOException , Exception{
-    rawDecoder.handleRect(x, y, w, h);
-    if (paint)
-      scheduleRepaint(x, y, w, h);
-  }
-
-  //
-  // Handle a CopyRect rectangle.
-  //
-
-  void handleCopyRect(int x, int y, int w, int h) throws IOException {
-    copyRectDecoder.handleRect(x, y, w, h);
-    scheduleRepaint(x, y, w, h);
-  }
-
-  //
-  // Handle an RRE-encoded rectangle.
-  //
-
-  void handleRRERect(int x, int y, int w, int h) throws IOException {
-    rreDecoder.handleRect(x, y, w, h);
-    scheduleRepaint(x, y, w, h);
-  }
-
-  //
-  // Handle a CoRRE-encoded rectangle.
-  //
-
-  void handleCoRRERect(int x, int y, int w, int h) throws IOException {
-    correDecoder.handleRect(x, y, w, h);
-    scheduleRepaint(x, y, w, h);
-  }
-
-  //
-  // Handle a Hextile-encoded rectangle.
-  //
-
-  void handleHextileRect(int x, int y, int w, int h) throws IOException,
-                                                            Exception {
-    hextileDecoder.handleRect(x, y, w, h);
-  }
-
-  //
-  // Handle a ZRLE-encoded rectangle.
-  //
-  // FIXME: Currently, session recording is not fully supported for ZRLE.
-  //
-
-  void handleZRLERect(int x, int y, int w, int h) throws Exception {
-    zrleDecoder.handleRect(x, y, w, h);
-    scheduleRepaint(x, y, w, h);
-  }
-
-  //
-  // Handle a Zlib-encoded rectangle.
-  //
-
-  void handleZlibRect(int x, int y, int w, int h) throws Exception {
-    zlibDecoder.handleRect(x, y, w, h);
-    scheduleRepaint(x, y, w, h);
-  }
-
-  //
-  // Handle a Tight-encoded rectangle.
-  //
-
-  void handleTightRect(int x, int y, int w, int h) throws Exception {
-    tightDecoder.handleRect(x, y, w, h);
-    scheduleRepaint(x, y, w, h);
-  }
-
-  //
-  // Tell JVM to repaint specified desktop area.
-  //
-
-  public void scheduleRepaint(int x, int y, int w, int h) {
-    // Request repaint, deferred if necessary.
-    if (rfb.framebufferWidth == scaledWidth) {
-      repaint(viewer.deferScreenUpdates, x, y, w, h);
-    } else {
-      int sx = x * scalingFactor / 100;
-      int sy = y * scalingFactor / 100;
-      int sw = ((x + w) * scalingFactor + 49) / 100 - sx + 1;
-      int sh = ((y + h) * scalingFactor + 49) / 100 - sy + 1;
-      repaint(viewer.deferScreenUpdates, sx, sy, sw, sh);
-    }
-  }
-
-  //
-  // Handle events.
-  //
-
-  public void keyPressed(KeyEvent evt) {
-    processLocalKeyEvent(evt);
-  }
-  public void keyReleased(KeyEvent evt) {
-    processLocalKeyEvent(evt);
-  }
-  public void keyTyped(KeyEvent evt) {
-    evt.consume();
-  }
-
-  public void mousePressed(MouseEvent evt) {
-    processLocalMouseEvent(evt, false);
-  }
-  public void mouseReleased(MouseEvent evt) {
-    processLocalMouseEvent(evt, false);
-  }
-  public void mouseMoved(MouseEvent evt) {
-    processLocalMouseEvent(evt, true);
-  }
-  public void mouseDragged(MouseEvent evt) {
-    processLocalMouseEvent(evt, true);
-  }
-
-  private synchronized void trySendPointerEvent() {
-    if ((needToSendMouseEvent) && (mouseEvent!=null)) {
-      sendMouseEvent(mouseEvent, false);
-      needToSendMouseEvent = false;
-      lastMouseEventSendTime = System.currentTimeMillis();
-    }
-  }
-
-  public void run() {
-    while (true) {
-      // Send mouse movement if we have it
-      trySendPointerEvent();
-      // Sleep for some time
-      try {
-        Thread.sleep(1000 / mouseMaxFreq);
-      } catch (InterruptedException ex) {
-      }
-    }
-  }
-
-  //
-  // Ignored events.
-  //
-
-  public void mouseClicked(MouseEvent evt) {}
-  public void mouseEntered(MouseEvent evt) {}
-  public void mouseExited(MouseEvent evt) {}
-
-  //
-  // Actual event processing.
-  //
-
-  private void processLocalKeyEvent(KeyEvent evt) {
-    if (viewer.rfb != null && rfb.inNormalProtocol) {
-      if (!inputEnabled) {
-	if ((evt.getKeyChar() == 'r' || evt.getKeyChar() == 'R') &&
-	    evt.getID() == KeyEvent.KEY_PRESSED ) {
-	  // Request screen update.
-	  try {
-	    rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth,
-					      rfb.framebufferHeight, false);
-	  } catch (IOException e) {
-	    e.printStackTrace();
-	  }
-	}
-      } else {
-	// Input enabled.
-	synchronized(rfb) {
-	  try {
-	    rfb.writeKeyEvent(evt);
-	  } catch (Exception e) {
-	    e.printStackTrace();
-	  }
-	  rfb.notify();
-	}
-      }
-    }
-    // Don't ever pass keyboard events to AWT for default processing.
-    // Otherwise, pressing Tab would switch focus to ButtonPanel etc.
-    evt.consume();
-  }
-
-  private void processLocalMouseEvent(MouseEvent evt, boolean moved) {
-    if (viewer.rfb != null && rfb.inNormalProtocol) {
-      if (inputEnabled) {
-        // If mouse not moved, but it's click event then
-        // send it to server immideanlty.
-        // Else, it's mouse movement - we can send it in
-        // our thread later.
-        if (!moved) {
-          sendMouseEvent(evt, moved);
-        } else {
-          mouseEvent = evt;
-          needToSendMouseEvent = true;
-        }
-      }
-    }
-  }
-
-  private void sendMouseEvent(MouseEvent evt, boolean moved) {
-    if (moved) {
-      softCursorMove(evt.getX(), evt.getY());
-    }
-    if (rfb.framebufferWidth != scaledWidth) {
-      int sx = (evt.getX() * 100 + scalingFactor/2) / scalingFactor;
-      int sy = (evt.getY() * 100 + scalingFactor/2) / scalingFactor;
-      evt.translatePoint(sx - evt.getX(), sy - evt.getY());
-    }
-    synchronized(rfb) {
-      try {
-        rfb.writePointerEvent(evt);
-      } catch (Exception e) {
-        e.printStackTrace();
-      }
-      rfb.notify();
-      lastMouseEventSendTime = System.currentTimeMillis();
-    }
-  }
-
-  //
-  // Reset update statistics.
-  //
-
-  void resetStats() {
-    statStartTime = System.currentTimeMillis();
-    statNumUpdates = 0;
-    statNumTotalRects = 0;
-    statNumPixelRects = 0;
-    statNumRectsTight = 0;
-    statNumRectsTightJPEG = 0;
-    statNumRectsZRLE = 0;
-    statNumRectsHextile = 0;
-    statNumRectsRaw = 0;
-    statNumRectsCopy = 0;
-    statNumBytesEncoded = 0;
-    statNumBytesDecoded = 0;
-    if (tightDecoder != null) {
-      tightDecoder.setNumJPEGRects(0);
-      tightDecoder.setNumTightRects(0);
-    }
-  }
-
-  //////////////////////////////////////////////////////////////////
-  //
-  // Handle cursor shape updates (XCursor and RichCursor encodings).
-  //
-
-  boolean showSoftCursor = false;
-
-  MemoryImageSource softCursorSource;
-  Image softCursor;
-  MouseEvent mouseEvent = null;
-  boolean needToSendMouseEvent = false;
-  int cursorX = 0, cursorY = 0;
-  int cursorWidth, cursorHeight;
-  int origCursorWidth, origCursorHeight;
-  int hotX, hotY;
-  int origHotX, origHotY;
-
-  //
-  // Handle cursor shape update (XCursor and RichCursor encodings).
-  //
-
-  synchronized void
-    handleCursorShapeUpdate(int encodingType,
-			    int xhot, int yhot, int width, int height)
-    throws IOException {
-
-    softCursorFree();
-
-    if (width * height == 0)
-      return;
-
-    // Ignore cursor shape data if requested by user.
-    if (viewer.options.ignoreCursorUpdates) {
-      int bytesPerRow = (width + 7) / 8;
-      int bytesMaskData = bytesPerRow * height;
-
-      if (encodingType == rfb.EncodingXCursor) {
-	rfb.skipBytes(6 + bytesMaskData * 2);
-      } else {
-	// rfb.EncodingRichCursor
-	rfb.skipBytes(width * height + bytesMaskData);
-      }
-      return;
-    }
-
-    // Decode cursor pixel data.
-    softCursorSource = decodeCursorShape(encodingType, width, height);
-
-    // Set original (non-scaled) cursor dimensions.
-    origCursorWidth = width;
-    origCursorHeight = height;
-    origHotX = xhot;
-    origHotY = yhot;
-
-    // Create off-screen cursor image.
-    createSoftCursor();
-
-    // Show the cursor.
-    showSoftCursor = true;
-    repaint(viewer.deferCursorUpdates,
-	    cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight);
-  }
-
-  //
-  // decodeCursorShape(). Decode cursor pixel data and return
-  // corresponding MemoryImageSource instance.
-  //
-
-  synchronized MemoryImageSource
-    decodeCursorShape(int encodingType, int width, int height)
-    throws IOException {
-
-    int bytesPerRow = (width + 7) / 8;
-    int bytesMaskData = bytesPerRow * height;
-
-    int[] softCursorPixels = new int[width * height];
-
-    if (encodingType == rfb.EncodingXCursor) {
-
-      // Read foreground and background colors of the cursor.
-      byte[] rgb = new byte[6];
-      rfb.readFully(rgb);
-      int[] colors = { (0xFF000000 | (rgb[3] & 0xFF) << 16 |
-			(rgb[4] & 0xFF) << 8 | (rgb[5] & 0xFF)),
-		       (0xFF000000 | (rgb[0] & 0xFF) << 16 |
-			(rgb[1] & 0xFF) << 8 | (rgb[2] & 0xFF)) };
-
-      // Read pixel and mask data.
-      byte[] pixBuf = new byte[bytesMaskData];
-      rfb.readFully(pixBuf);
-      byte[] maskBuf = new byte[bytesMaskData];
-      rfb.readFully(maskBuf);
-
-      // Decode pixel data into softCursorPixels[].
-      byte pixByte, maskByte;
-      int x, y, n, result;
-      int i = 0;
-      for (y = 0; y < height; y++) {
-	for (x = 0; x < width / 8; x++) {
-	  pixByte = pixBuf[y * bytesPerRow + x];
-	  maskByte = maskBuf[y * bytesPerRow + x];
-	  for (n = 7; n >= 0; n--) {
-	    if ((maskByte >> n & 1) != 0) {
-	      result = colors[pixByte >> n & 1];
-	    } else {
-	      result = 0;	// Transparent pixel
-	    }
-	    softCursorPixels[i++] = result;
-	  }
-	}
-	for (n = 7; n >= 8 - width % 8; n--) {
-	  if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) {
-	    result = colors[pixBuf[y * bytesPerRow + x] >> n & 1];
-	  } else {
-	    result = 0;		// Transparent pixel
-	  }
-	  softCursorPixels[i++] = result;
-	}
-      }
-
-    } else {
-      // encodingType == rfb.EncodingRichCursor
-
-      // Read pixel and mask data.
-      byte[] pixBuf = new byte[width * height * bytesPixel];
-      rfb.readFully(pixBuf);
-      byte[] maskBuf = new byte[bytesMaskData];
-      rfb.readFully(maskBuf);
-
-      // Decode pixel data into softCursorPixels[].
-      byte maskByte;
-      int x, y, n, result;
-      int i = 0;
-      for (y = 0; y < height; y++) {
-	for (x = 0; x < width / 8; x++) {
-	  maskByte = maskBuf[y * bytesPerRow + x];
-	  for (n = 7; n >= 0; n--) {
-	    if ((maskByte >> n & 1) != 0) {
-	      if (bytesPixel == 1) {
-		result = cm8.getRGB(pixBuf[i]);
-	      } else {
-		result = 0xFF000000 |
-		  (pixBuf[i * 4 + 2] & 0xFF) << 16 |
-		  (pixBuf[i * 4 + 1] & 0xFF) << 8 |
-		  (pixBuf[i * 4] & 0xFF);
-	      }
-	    } else {
-	      result = 0;	// Transparent pixel
-	    }
-	    softCursorPixels[i++] = result;
-	  }
-	}
-	for (n = 7; n >= 8 - width % 8; n--) {
-	  if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) {
-	    if (bytesPixel == 1) {
-	      result = cm8.getRGB(pixBuf[i]);
-	    } else {
-	      result = 0xFF000000 |
-		(pixBuf[i * 4 + 2] & 0xFF) << 16 |
-		(pixBuf[i * 4 + 1] & 0xFF) << 8 |
-		(pixBuf[i * 4] & 0xFF);
-	    }
-	  } else {
-	    result = 0;		// Transparent pixel
-	  }
-	  softCursorPixels[i++] = result;
-	}
-      }
-
-    }
-
-    return new MemoryImageSource(width, height, softCursorPixels, 0, width);
-  }
-
-  //
-  // createSoftCursor(). Assign softCursor new Image (scaled if necessary).
-  // Uses softCursorSource as a source for new cursor image.
-  //
-
-  synchronized void
-    createSoftCursor() {
-
-    if (softCursorSource == null)
-      return;
-
-    int scaleCursor = viewer.options.scaleCursor;
-    if (scaleCursor == 0 || !inputEnabled)
-      scaleCursor = 100;
-
-    // Save original cursor coordinates.
-    int x = cursorX - hotX;
-    int y = cursorY - hotY;
-    int w = cursorWidth;
-    int h = cursorHeight;
-
-    cursorWidth = (origCursorWidth * scaleCursor + 50) / 100;
-    cursorHeight = (origCursorHeight * scaleCursor + 50) / 100;
-    hotX = (origHotX * scaleCursor + 50) / 100;
-    hotY = (origHotY * scaleCursor + 50) / 100;
-    softCursor = Toolkit.getDefaultToolkit().createImage(softCursorSource);
-
-    if (scaleCursor != 100) {
-      softCursor = softCursor.getScaledInstance(cursorWidth, cursorHeight,
-						Image.SCALE_SMOOTH);
-    }
-
-    if (showSoftCursor) {
-      // Compute screen area to update.
-      x = Math.min(x, cursorX - hotX);
-      y = Math.min(y, cursorY - hotY);
-      w = Math.max(w, cursorWidth);
-      h = Math.max(h, cursorHeight);
-
-      repaint(viewer.deferCursorUpdates, x, y, w, h);
-    }
-  }
-
-  //
-  // softCursorMove(). Moves soft cursor into a particular location.
-  //
-
-  synchronized void softCursorMove(int x, int y) {
-    int oldX = cursorX;
-    int oldY = cursorY;
-    cursorX = x;
-    cursorY = y;
-    if (showSoftCursor) {
-      repaint(viewer.deferCursorUpdates,
-	      oldX - hotX, oldY - hotY, cursorWidth, cursorHeight);
-      repaint(viewer.deferCursorUpdates,
-	      cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight);
-    }
-  }
-
-  //
-  // softCursorFree(). Remove soft cursor, dispose resources.
-  //
-
-  synchronized void softCursorFree() {
-    if (showSoftCursor) {
-      showSoftCursor = false;
-      softCursor = null;
-      softCursorSource = null;
-
-      repaint(viewer.deferCursorUpdates,
-	      cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight);
-    }
-  }
-}
diff --git a/java/src/com/tigervnc/vncviewer/VncCanvas2.java b/java/src/com/tigervnc/vncviewer/VncCanvas2.java
deleted file mode 100644
index c39d4a5..0000000
--- a/java/src/com/tigervnc/vncviewer/VncCanvas2.java
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-//  Copyright (C) 2006 Constantin Kaplinsky.  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.vncviewer;
-
-import java.awt.*;
-import java.io.*;
-
-//
-// VncCanvas2 is a special version of VncCanvas which may use Java 2 API.
-//
-
-class VncCanvas2 extends VncCanvas {
-
-  public VncCanvas2(VncViewer v) throws IOException {
-    super(v);
-    disableFocusTraversalKeys();
-  }
-
-  public VncCanvas2(VncViewer v, int maxWidth_, int maxHeight_)
-    throws IOException {
-
-    super(v, maxWidth_, maxHeight_);
-    disableFocusTraversalKeys();
-  }
-
-  public void paintScaledFrameBuffer(Graphics g) {
-    Graphics2D g2d = (Graphics2D)g;
-    g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
-                         RenderingHints.VALUE_RENDER_QUALITY);
-    g2d.drawImage(memImage, 0, 0, scaledWidth, scaledHeight, null);
-  }
-
-  //
-  // Try to disable focus traversal keys (JVMs 1.4 and higher).
-  //
-
-  private void disableFocusTraversalKeys() {
-    try {
-      Class[] argClasses = { Boolean.TYPE };
-      java.lang.reflect.Method method =
-        getClass().getMethod("setFocusTraversalKeysEnabled", argClasses);
-      Object[] argObjects = { new Boolean(false) };
-      method.invoke(this, argObjects);
-    } catch (Exception e) {}
-  }
-
-}
-
diff --git a/java/src/com/tigervnc/vncviewer/VncViewer.java b/java/src/com/tigervnc/vncviewer/VncViewer.java
index 3a9f4c6..ba54918 100644
--- a/java/src/com/tigervnc/vncviewer/VncViewer.java
+++ b/java/src/com/tigervnc/vncviewer/VncViewer.java
@@ -1,1106 +1,268 @@
-//
-//  Copyright (C) 2001-2004 HorizonLive.com, Inc.  All Rights Reserved.
-//  Copyright (C) 2002 Constantin Kaplinsky.  All Rights Reserved.
-//  Copyright (C) 1999 AT&T Laboratories Cambridge.  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.
-//
+/* 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.
+ */
 
 //
-// VncViewer.java - the VNC viewer applet.  This class mainly just sets up the
-// user interface, leaving it to the VncCanvas to do the actual rendering of
-// a VNC desktop.
+// VncViewer - the VNC viewer applet.  It can also be run from the
+// command-line, when it behaves as much as possibly like the windows and unix
+// viewers.
 //
+// Unfortunately, because of the way Java classes are loaded on demand, only
+// configuration parameters defined in this file can be set from the command
+// line or in applet parameters.
 
 package com.tigervnc.vncviewer;
 
 import java.awt.*;
-import java.awt.event.*;
-import java.io.*;
-import java.net.*;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.image.*;
+import java.awt.Label;
+import javax.swing.*;
+import java.net.URL;
 
-public class VncViewer extends java.applet.Applet
-  implements java.lang.Runnable, WindowListener, ComponentListener {
+import com.tigervnc.rdr.*;
+import com.tigervnc.rfb.*;
+import com.tigervnc.rfb.Exception;
 
-  boolean inAnApplet = true;
-  boolean inSeparateFrame = false;
-
-  //
-  // main() is called when run as a java program from the command line.
-  // It simply runs the applet inside a newly-created frame.
-  //
+public class VncViewer extends java.applet.Applet implements Runnable
+{
+  public static final String version = "1.0.90";
+  public static final String about1 = "TigerVNC Viewer for Java "+version;
+  public static final String about2 = "Copyright (C) 1998-2010 "+
+                                      "[many holders]";
+  public static final String about3 = "Visit www.tigervnc.org "+
+                                      "for information on TigerVNC.";
 
   public static void main(String[] argv) {
-    VncViewer v = new VncViewer();
-    v.mainArgs = argv;
-    v.inAnApplet = false;
-    v.inSeparateFrame = true;
-
-    v.init();
-    v.start();
-  }
-
-  String[] mainArgs;
-
-  RfbProto rfb;
-  Thread rfbThread;
-
-  Frame vncFrame;
-  Container vncContainer;
-  ScrollPane desktopScrollPane;
-  GridBagLayout gridbag;
-  ButtonPanel buttonPanel;
-  Label connStatusLabel;
-  VncCanvas vc;
-  OptionsFrame options;
-  ClipboardFrame clipboard;
-  RecordingFrame rec;
-
-  // Control session recording.
-  Object recordingSync;
-  String sessionFileName;
-  boolean recordingActive;
-  boolean recordingStatusChanged;
-  String cursorUpdatesDef;
-  String eightBitColorsDef;
-
-  // Variables read from parameter values.
-  String socketFactory;
-  String host;
-  int port;
-  String passwordParam;
-  boolean showControls;
-  boolean offerRelogin;
-  boolean showOfflineDesktop;
-  int deferScreenUpdates;
-  int deferCursorUpdates;
-  int deferUpdateRequests;
-  int debugStatsExcludeUpdates;
-  int debugStatsMeasureUpdates;
-
-  // Reference to this applet for inter-applet communication.
-  public static java.applet.Applet refApplet;
-
-  //
-  // init()
-  //
-
-  public void init() {
-
-    readParameters();
-
-    refApplet = this;
-
-    if (inSeparateFrame) {
-      vncFrame = new Frame("TigerVNC");
-      if (!inAnApplet) {
-	vncFrame.add("Center", this);
-      }
-      vncContainer = vncFrame;
-    } else {
-      vncContainer = this;
-    }
-
-    recordingSync = new Object();
-
-    options = new OptionsFrame(this);
-    clipboard = new ClipboardFrame(this);
-    if (RecordingFrame.checkSecurity())
-      rec = new RecordingFrame(this);
-
-    sessionFileName = null;
-    recordingActive = false;
-    recordingStatusChanged = false;
-    cursorUpdatesDef = null;
-    eightBitColorsDef = null;
-
-    if (inSeparateFrame) {
-      vncFrame.addWindowListener(this);
-      vncFrame.addComponentListener(this);
-    }
-
-    rfbThread = new Thread(this);
-    rfbThread.start();
-  }
-
-  public void update(Graphics g) {
-  }
-
-  //
-  // run() - executed by the rfbThread to deal with the RFB socket.
-  //
-
-  public void run() {
-
-    gridbag = new GridBagLayout();
-    vncContainer.setLayout(gridbag);
-
-    GridBagConstraints gbc = new GridBagConstraints();
-    gbc.gridwidth = GridBagConstraints.REMAINDER;
-    gbc.anchor = GridBagConstraints.NORTHWEST;
-
-    if (showControls) {
-      buttonPanel = new ButtonPanel(this);
-      gridbag.setConstraints(buttonPanel, gbc);
-      vncContainer.add(buttonPanel);
-    }
-
     try {
-      connectAndAuthenticate();
-      doProtocolInitialisation();
-
-      // FIXME: Use auto-scaling not only in a separate frame.
-      if (options.autoScale && inSeparateFrame) {
-	Dimension screenSize;
-	try {
-	  screenSize = vncContainer.getToolkit().getScreenSize();
-	} catch (Exception e) {
-	  screenSize = new Dimension(0, 0);
-	}
-	createCanvas(screenSize.width - 32, screenSize.height - 32);
+      String os = System.getProperty("os.name");
+      if (os.startsWith("Windows")) {
+        String laf = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
+        UIManager.setLookAndFeel(laf);
       } else {
-	createCanvas(0, 0);
-      }
-
-      gbc.weightx = 1.0;
-      gbc.weighty = 1.0;
-
-      if (inSeparateFrame) {
-
-	// Create a panel which itself is resizeable and can hold
-	// non-resizeable VncCanvas component at the top left corner.
-	Panel canvasPanel = new Panel();
-	canvasPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
-	canvasPanel.add(vc);
-
-	// Create a ScrollPane which will hold a panel with VncCanvas
-	// inside.
-	desktopScrollPane = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED);
-	gbc.fill = GridBagConstraints.BOTH;
-	gridbag.setConstraints(desktopScrollPane, gbc);
-	desktopScrollPane.add(canvasPanel);
-        // If auto scale is not enabled we don't need to set first frame
-        // size to fullscreen
-        if (!options.autoScale) {
-          vc.isFirstSizeAutoUpdate = false;
+        UIManager.put("swing.boldMetal", Boolean.FALSE);
+        javax.swing.plaf.FontUIResource f = new
+          javax.swing.plaf.FontUIResource("SansSerif", Font.PLAIN, 11);
+        java.util.Enumeration keys = UIManager.getDefaults().keys();
+        while (keys.hasMoreElements()) {
+          Object key = keys.nextElement();
+          Object value = UIManager.get (key);
+          if (value instanceof javax.swing.plaf.FontUIResource)
+            UIManager.put(key, f);
         }
-
-	// Finally, add our ScrollPane to the Frame window.
-	vncFrame.add(desktopScrollPane);
-	vncFrame.setTitle(rfb.desktopName);
-	vncFrame.pack();
-	vc.resizeDesktopFrame();
-
-      } else {
-	// Just add the VncCanvas component to the Applet.
-	gridbag.setConstraints(vc, gbc);
-	add(vc);
-	validate();
       }
+      UIManager.put("TitledBorder.titleColor",Color.blue);
+    } catch (java.lang.Exception exc) { }
+    VncViewer viewer = new VncViewer(argv);
+    viewer.start();
+  }
 
-      if (showControls) {
-        buttonPanel.enableButtons();
-      }
-
-      moveFocusToDesktop();
-      processNormalProtocol();
-
-    } catch (NoRouteToHostException e) {
-      fatalError("Network error: no route to server: " + host, e);
-    } catch (UnknownHostException e) {
-      fatalError("Network error: server name unknown: " + host, e);
-    } catch (ConnectException e) {
-      fatalError("Network error: could not connect to server: " +
-		 host + ":" + port, e);
-    } catch (EOFException e) {
-      if (showOfflineDesktop) {
-	e.printStackTrace();
-	System.out.println("Network error: remote side closed connection");
-	if (vc != null) {
-	  vc.enableInput(false);
-	}
-	if (inSeparateFrame) {
-	  vncFrame.setTitle(rfb.desktopName + " [disconnected]");
-	}
-	if (rfb != null && !rfb.closed())
-	  rfb.close();
-	if (showControls && buttonPanel != null) {
-	  buttonPanel.disableButtonsOnDisconnect();
-	  if (inSeparateFrame) {
-	    vncFrame.pack();
-	  } else {
-	    validate();
-	  }
-	}
-      } else {
-	fatalError("Network error: remote side closed connection", e);
-      }
-    } catch (IOException e) {
-      String str = e.getMessage();
-      if (str != null && str.length() != 0) {
-	fatalError("Network Error: " + str, e);
-      } else {
-	fatalError(e.toString(), e);
-      }
-    } catch (Exception e) {
-      String str = e.getMessage();
-      if (str != null && str.length() != 0) {
-	fatalError("Error: " + str, e);
-      } else {
-	fatalError(e.toString(), e);
-      }
-    }
+  
+  public VncViewer(String[] argv) {
+    applet = false;
     
-  }
-
-  //
-  // Create a VncCanvas instance.
-  //
-
-  void createCanvas(int maxWidth, int maxHeight) throws IOException {
-    // Determine if Java 2D API is available and use a special
-    // version of VncCanvas if it is present.
-    vc = null;
-    try {
-      // This throws ClassNotFoundException if there is no Java 2D API.
-      Class cl = Class.forName("java.awt.Graphics2D");
-      // If we could load Graphics2D class, then we can use VncCanvas2D.
-      cl = Class.forName("com.tigervnc.vncviewer.VncCanvas2");
-      Class[] argClasses = { this.getClass(), Integer.TYPE, Integer.TYPE };
-      java.lang.reflect.Constructor cstr = cl.getConstructor(argClasses);
-      Object[] argObjects =
-        { this, new Integer(maxWidth), new Integer(maxHeight) };
-      vc = (VncCanvas)cstr.newInstance(argObjects);
-    } catch (Exception e) {
-      System.out.println("Warning: Java 2D API is not available");
-    }
-
-    // If we failed to create VncCanvas2D, use old VncCanvas.
-    if (vc == null)
-      vc = new VncCanvas(this, maxWidth, maxHeight);
-  }
-
-
-  //
-  // Process RFB socket messages.
-  // If the rfbThread is being stopped, ignore any exceptions,
-  // otherwise rethrow the exception so it can be handled.
-  //
- 
-  void processNormalProtocol() throws Exception {
-    try {
-      vc.processNormalProtocol();
-    } catch (Exception e) {
-      if (rfbThread == null) {
-	System.out.println("Ignoring RFB socket exceptions" +
-			   " because applet is stopping");
-      } else {
-	throw e;
-      }
-    }
-  }
-
-
-  //
-  // Connect to the RFB server and authenticate the user.
-  //
-
-  void connectAndAuthenticate() throws Exception
-  {
-    showConnectionStatus("Initializing...");
-    if (inSeparateFrame) {
-      vncFrame.pack();
-      vncFrame.show();
-    } else {
-      validate();
-    }
-
-    showConnectionStatus("Connecting to " + host + ", port " + port + "...");
-
-    rfb = new RfbProto(host, port, this);
-    showConnectionStatus("Connected to server");
-
-    rfb.readVersionMsg();
-    showConnectionStatus("RFB server supports protocol version " +
-			 rfb.serverMajor + "." + rfb.serverMinor);
-
-    rfb.writeVersionMsg();
-    showConnectionStatus("Using RFB protocol version " +
-			 rfb.clientMajor + "." + rfb.clientMinor);
-
-    int secType = rfb.negotiateSecurity();
-    doAuthentification(secType);
-  }
-
-    void doAuthentification(int secType) throws Exception {
-	switch (secType) {
-	case RfbProto.SecTypeNone:
-	    showConnectionStatus("No authentication needed");
-	    rfb.authenticateNone();
-	    break;
-	case RfbProto.SecTypeVncAuth:
-	    showConnectionStatus("Performing standard VNC authentication");
-	    if (passwordParam != null) {
-		rfb.authenticateVNC(passwordParam);
-	    } else {
-		String pw = askPassword();
-		rfb.authenticateVNC(pw);
-	    }
-	    break;
-	case RfbProto.SecTypeVeNCrypt:
-	    showConnectionStatus("VeNCrypt chooser");
-	    secType = rfb.authenticateVeNCrypt();
-	    doAuthentification(secType);
-	    break;
-	case RfbProto.SecTypePlain:
-	    showConnectionStatus("Plain authentication");
-	    {
-		String user = askUser();
-		String pw = askPassword();
-		rfb.authenticatePlain(user,pw);
-	    }
-	    break;
-	case RfbProto.SecTypeTLSNone:
-	    showConnectionStatus("TLSNone");
-	    rfb.authenticateTLS();
-	    rfb.authenticateNone();
-	    break;
-	case RfbProto.SecTypeTLSVnc:
-	    showConnectionStatus("TLSVnc");
-	    rfb.authenticateTLS();
-	    doAuthentification(RfbProto.SecTypeVncAuth);
-	    break;
-	case RfbProto.SecTypeTLSPlain:
-	    showConnectionStatus("TLSPlain");
-	    rfb.authenticateTLS();
-	    doAuthentification(RfbProto.SecTypePlain);
-	    break;
-	case RfbProto.SecTypeX509None:
-	    showConnectionStatus("X509None");
-	    rfb.authenticateX509();
-	    rfb.authenticateNone();
-	    break;
-	case RfbProto.SecTypeX509Vnc:
-	    showConnectionStatus("X509Vnc");
-	    rfb.authenticateX509();
-	    doAuthentification(RfbProto.SecTypeVncAuth);
-	    break;
-	case RfbProto.SecTypeX509Plain:
-	    showConnectionStatus("X509Plain");
-	    rfb.authenticateX509();
-	    doAuthentification(RfbProto.SecTypePlain);
-	    break;
-	default:
-	    throw new Exception("Unknown authentication scheme " + secType);
-	}
-    }
-
-
-  //
-  // Show a message describing the connection status.
-  // To hide the connection status label, use (msg == null).
-  //
-
-  void showConnectionStatus(String msg)
-  {
-    if (msg == null) {
-      if (vncContainer.isAncestorOf(connStatusLabel)) {
-	vncContainer.remove(connStatusLabel);
-      }
-      return;
-    }
-
-    System.out.println(msg);
-
-    if (connStatusLabel == null) {
-      connStatusLabel = new Label("Status: " + msg);
-      connStatusLabel.setFont(new Font("Helvetica", Font.PLAIN, 12));
-    } else {
-      connStatusLabel.setText("Status: " + msg);
-    }
-
-    if (!vncContainer.isAncestorOf(connStatusLabel)) {
-      GridBagConstraints gbc = new GridBagConstraints();
-      gbc.gridwidth = GridBagConstraints.REMAINDER;
-      gbc.fill = GridBagConstraints.HORIZONTAL;
-      gbc.anchor = GridBagConstraints.NORTHWEST;
-      gbc.weightx = 1.0;
-      gbc.weighty = 1.0;
-      gbc.insets = new Insets(20, 30, 20, 30);
-      gridbag.setConstraints(connStatusLabel, gbc);
-      vncContainer.add(connStatusLabel);
-    }
-
-    if (inSeparateFrame) {
-      vncFrame.pack();
-    } else {
-      validate();
-    }
-  }
-
-
-  //
-  // Show an authentication panel.
-  //
-
-  String askUser() throws Exception
-  {
-    showConnectionStatus(null);
-
-    AuthPanel authPanel = new AuthPanel(this, false);
-
-    GridBagConstraints gbc = new GridBagConstraints();
-    gbc.gridwidth = GridBagConstraints.REMAINDER;
-    gbc.anchor = GridBagConstraints.NORTHWEST;
-    gbc.weightx = 1.0;
-    gbc.weighty = 1.0;
-    gbc.ipadx = 100;
-    gbc.ipady = 50;
-    gridbag.setConstraints(authPanel, gbc);
-    vncContainer.add(authPanel);
-
-    if (inSeparateFrame) {
-      vncFrame.pack();
-    } else {
-      validate();
-    }
-
-    authPanel.moveFocusToDefaultField();
-    String pw = authPanel.getPassword();
-    vncContainer.remove(authPanel);
-
-    return pw;
-  }
-
-  String askPassword() throws Exception
-  {
-    showConnectionStatus(null);
-
-    AuthPanel authPanel = new AuthPanel(this, true);
-
-    GridBagConstraints gbc = new GridBagConstraints();
-    gbc.gridwidth = GridBagConstraints.REMAINDER;
-    gbc.anchor = GridBagConstraints.NORTHWEST;
-    gbc.weightx = 1.0;
-    gbc.weighty = 1.0;
-    gbc.ipadx = 100;
-    gbc.ipady = 50;
-    gridbag.setConstraints(authPanel, gbc);
-    vncContainer.add(authPanel);
-
-    if (inSeparateFrame) {
-      vncFrame.pack();
-    } else {
-      validate();
-    }
-
-    authPanel.moveFocusToDefaultField();
-    String pw = authPanel.getPassword();
-    vncContainer.remove(authPanel);
-
-    return pw;
-  }
-
-
-  //
-  // Do the rest of the protocol initialisation.
-  //
-
-  void doProtocolInitialisation() throws IOException
-  {
-    rfb.writeClientInit();
-    rfb.readServerInit();
-
-    System.out.println("Desktop name is " + rfb.desktopName);
-    System.out.println("Desktop size is " + rfb.framebufferWidth + " x " +
-		       rfb.framebufferHeight);
-
-    setEncodings();
-
-    showConnectionStatus(null);
-  }
-
-
-  //
-  // Send current encoding list to the RFB server.
-  //
-
-  int[] encodingsSaved;
-  int nEncodingsSaved;
-
-  void setEncodings()        { setEncodings(false); }
-  void autoSelectEncodings() { setEncodings(true); }
-
-  void setEncodings(boolean autoSelectOnly) {
-    if (options == null || rfb == null || !rfb.inNormalProtocol)
-      return;
-
-    int preferredEncoding = options.preferredEncoding;
-    if (preferredEncoding == -1) {
-      long kbitsPerSecond = rfb.kbitsPerSecond();
-      if (nEncodingsSaved < 1) {
-        // Choose Tight or ZRLE encoding for the very first update.
-        System.out.println("Using Tight/ZRLE encodings");
-        preferredEncoding = RfbProto.EncodingTight;
-      } else if (kbitsPerSecond > 2000 &&
-                 encodingsSaved[0] != RfbProto.EncodingHextile) {
-        // Switch to Hextile if the connection speed is above 2Mbps.
-        System.out.println("Throughput " + kbitsPerSecond +
-                           " kbit/s - changing to Hextile encoding");
-        preferredEncoding = RfbProto.EncodingHextile;
-      } else if (kbitsPerSecond < 1000 &&
-                 encodingsSaved[0] != RfbProto.EncodingTight) {
-        // Switch to Tight/ZRLE if the connection speed is below 1Mbps.
-        System.out.println("Throughput " + kbitsPerSecond +
-                           " kbit/s - changing to Tight/ZRLE encodings");
-        preferredEncoding = RfbProto.EncodingTight;
-      } else {
-        // Don't change the encoder.
-        if (autoSelectOnly)
-          return;
-        preferredEncoding = encodingsSaved[0];
-      }
-    } else {
-      // Auto encoder selection is not enabled.
-      if (autoSelectOnly)
-        return;
-    }
-
-    int[] encodings = new int[20];
-    int nEncodings = 0;
-
-    encodings[nEncodings++] = preferredEncoding;
-    if (options.useCopyRect) {
-      encodings[nEncodings++] = RfbProto.EncodingCopyRect;
-    }
-
-    if (preferredEncoding != RfbProto.EncodingTight) {
-      encodings[nEncodings++] = RfbProto.EncodingTight;
-    }
-    if (preferredEncoding != RfbProto.EncodingZRLE) {
-      encodings[nEncodings++] = RfbProto.EncodingZRLE;
-    }
-    if (preferredEncoding != RfbProto.EncodingHextile) {
-      encodings[nEncodings++] = RfbProto.EncodingHextile;
-    }
-    if (preferredEncoding != RfbProto.EncodingZlib) {
-      encodings[nEncodings++] = RfbProto.EncodingZlib;
-    }
-    if (preferredEncoding != RfbProto.EncodingCoRRE) {
-      encodings[nEncodings++] = RfbProto.EncodingCoRRE;
-    }
-    if (preferredEncoding != RfbProto.EncodingRRE) {
-      encodings[nEncodings++] = RfbProto.EncodingRRE;
-    }
-
-    if (options.compressLevel >= 0 && options.compressLevel <= 9) {
-      encodings[nEncodings++] =
-        RfbProto.EncodingCompressLevel0 + options.compressLevel;
-    }
-    if (options.jpegQuality >= 0 && options.jpegQuality <= 9) {
-      encodings[nEncodings++] =
-        RfbProto.EncodingQualityLevel0 + options.jpegQuality;
-    }
-
-    if (options.requestCursorUpdates) {
-      encodings[nEncodings++] = RfbProto.EncodingXCursor;
-      encodings[nEncodings++] = RfbProto.EncodingRichCursor;
-      if (!options.ignoreCursorUpdates)
-	encodings[nEncodings++] = RfbProto.EncodingPointerPos;
-    }
-
-    encodings[nEncodings++] = RfbProto.EncodingLastRect;
-    encodings[nEncodings++] = RfbProto.EncodingNewFBSize;
-
-    boolean encodingsWereChanged = false;
-    if (nEncodings != nEncodingsSaved) {
-      encodingsWereChanged = true;
-    } else {
-      for (int i = 0; i < nEncodings; i++) {
-        if (encodings[i] != encodingsSaved[i]) {
-          encodingsWereChanged = true;
-          break;
-        }
-      }
-    }
-
-    if (encodingsWereChanged) {
-      try {
-        rfb.writeSetEncodings(encodings, nEncodings);
-        if (vc != null) {
-          vc.softCursorFree();
-        }
-      } catch (Exception e) {
-        e.printStackTrace();
-      }
-      encodingsSaved = encodings;
-      nEncodingsSaved = nEncodings;
-    }
-  }
-
-
-  //
-  // setCutText() - send the given cut text to the RFB server.
-  //
-
-  void setCutText(String text) {
-    try {
-      if (rfb != null && rfb.inNormalProtocol) {
-	rfb.writeClientCutText(text);
-      }
-    } catch (Exception e) {
-      e.printStackTrace();
-    }
-  }
-
-
-  //
-  // Order change in session recording status. To stop recording, pass
-  // null in place of the fname argument.
-  //
-
-  void setRecordingStatus(String fname) {
-    synchronized(recordingSync) {
-      sessionFileName = fname;
-      recordingStatusChanged = true;
-    }
-  }
-
-  //
-  // Start or stop session recording. Returns true if this method call
-  // causes recording of a new session.
-  //
-
-  boolean checkRecordingStatus() throws IOException {
-    synchronized(recordingSync) {
-      if (recordingStatusChanged) {
-	recordingStatusChanged = false;
-	if (sessionFileName != null) {
-	  startRecording();
-	  return true;
-	} else {
-	  stopRecording();
-	}
-      }
-    }
-    return false;
-  }
-
-  //
-  // Start session recording.
-  //
-
-  protected void startRecording() throws IOException {
-    synchronized(recordingSync) {
-      if (!recordingActive) {
-	// Save settings to restore them after recording the session.
-	cursorUpdatesDef =
-	  options.choices[options.cursorUpdatesIndex].getSelectedItem();
-	eightBitColorsDef =
-	  options.choices[options.eightBitColorsIndex].getSelectedItem();
-	// Set options to values suitable for recording.
-	options.choices[options.cursorUpdatesIndex].select("Disable");
-	options.choices[options.cursorUpdatesIndex].setEnabled(false);
-	options.setEncodings();
-	options.choices[options.eightBitColorsIndex].select("No");
-	options.choices[options.eightBitColorsIndex].setEnabled(false);
-	options.setColorFormat();
-      } else {
-	rfb.closeSession();
+    // Override defaults with command-line options
+    for (int i = 0; i < argv.length; i++) {
+      if (argv[i].equalsIgnoreCase("-log")) {
+        if (++i >= argv.length) usage();
+        System.err.println("Log setting: "+argv[i]);
+        LogWriter.setLogParams(argv[i]);
+        continue;
       }
 
-      System.out.println("Recording the session in " + sessionFileName);
-      rfb.startSession(sessionFileName);
-      recordingActive = true;
-    }
-  }
+      if (Configuration.setParam(argv[i]))
+        continue;
 
-  //
-  // Stop session recording.
-  //
-
-  protected void stopRecording() throws IOException {
-    synchronized(recordingSync) {
-      if (recordingActive) {
-	// Restore options.
-	options.choices[options.cursorUpdatesIndex].select(cursorUpdatesDef);
-	options.choices[options.cursorUpdatesIndex].setEnabled(true);
-	options.setEncodings();
-	options.choices[options.eightBitColorsIndex].select(eightBitColorsDef);
-	options.choices[options.eightBitColorsIndex].setEnabled(true);
-	options.setColorFormat();
-
-	rfb.closeSession();
-	System.out.println("Session recording stopped.");
-      }
-      sessionFileName = null;
-      recordingActive = false;
-    }
-  }
-
-
-  //
-  // readParameters() - read parameters from the html source or from the
-  // command line.  On the command line, the arguments are just a sequence of
-  // param_name/param_value pairs where the names and values correspond to
-  // those expected in the html applet tag source.
-  //
-
-  void readParameters() {
-    host = readParameter("HOST", !inAnApplet);
-    if (host == null) {
-      host = getCodeBase().getHost();
-      if (host.equals("")) {
-	fatalError("HOST parameter not specified");
-      }
-    }
-
-    port = readIntParameter("PORT", 5900);
-
-    // Read "ENCPASSWORD" or "PASSWORD" parameter if specified.
-    readPasswordParameters();
-
-    String str;
-    if (inAnApplet) {
-      str = readParameter("Open New Window", false);
-      if (str != null && str.equalsIgnoreCase("Yes"))
-	inSeparateFrame = true;
-    }
-
-    // "Show Controls" set to "No" disables button panel.
-    showControls = true;
-    str = readParameter("Show Controls", false);
-    if (str != null && str.equalsIgnoreCase("No"))
-      showControls = false;
-
-    // "Offer Relogin" set to "No" disables "Login again" and "Close
-    // window" buttons under error messages in applet mode.
-    offerRelogin = true;
-    str = readParameter("Offer Relogin", false);
-    if (str != null && str.equalsIgnoreCase("No"))
-      offerRelogin = false;
-
-    // Do we continue showing desktop on remote disconnect?
-    showOfflineDesktop = false;
-    str = readParameter("Show Offline Desktop", false);
-    if (str != null && str.equalsIgnoreCase("Yes"))
-      showOfflineDesktop = true;
-
-    // Fine tuning options.
-    deferScreenUpdates = readIntParameter("Defer screen updates", 20);
-    deferCursorUpdates = readIntParameter("Defer cursor updates", 10);
-    deferUpdateRequests = readIntParameter("Defer update requests", 0);
-
-    // Debugging options.
-    debugStatsExcludeUpdates = readIntParameter("DEBUG_XU", 0);
-    debugStatsMeasureUpdates = readIntParameter("DEBUG_CU", 0);
-
-    // SocketFactory.
-    socketFactory = readParameter("SocketFactory", false);
-  }
-
-  //
-  // Read password parameters. If an "ENCPASSWORD" parameter is set,
-  // then decrypt the password into the passwordParam string. Otherwise,
-  // try to read the "PASSWORD" parameter directly to passwordParam.
-  //
-
-  private void readPasswordParameters() {
-    String encPasswordParam = readParameter("ENCPASSWORD", false);
-    if (encPasswordParam == null) {
-      passwordParam = readParameter("PASSWORD", false);
-    } else {
-      // ENCPASSWORD is hexascii-encoded. Decode.
-      byte[] pw = {0, 0, 0, 0, 0, 0, 0, 0};
-      int len = encPasswordParam.length() / 2;
-      if (len > 8)
-        len = 8;
-      for (int i = 0; i < len; i++) {
-        String hex = encPasswordParam.substring(i*2, i*2+2);
-        Integer x = new Integer(Integer.parseInt(hex, 16));
-        pw[i] = x.byteValue();
-      }
-      // Decrypt the password.
-      byte[] key = {23, 82, 107, 6, 35, 78, 88, 7};
-      DesCipher des = new DesCipher(key);
-      des.decrypt(pw, 0, pw, 0);
-      passwordParam = new String(pw);
-    }
-  }
-
-  public String readParameter(String name, boolean required) {
-    if (inAnApplet) {
-      String s = getParameter(name);
-      if ((s == null) && required) {
-	fatalError(name + " parameter not specified");
-      }
-      return s;
-    }
-
-    for (int i = 0; i < mainArgs.length; i += 2) {
-      if (mainArgs[i].equalsIgnoreCase(name)) {
-	try {
-	  return mainArgs[i+1];
-	} catch (Exception e) {
-	  if (required) {
-	    fatalError(name + " parameter not specified");
-	  }
-	  return null;
-	}
-      }
-    }
-    if (required) {
-      fatalError(name + " parameter not specified");
-    }
-    return null;
-  }
-
-  int readIntParameter(String name, int defaultValue) {
-    String str = readParameter(name, false);
-    int result = defaultValue;
-    if (str != null) {
-      try {
-	result = Integer.parseInt(str);
-      } catch (NumberFormatException e) { }
-    }
-    return result;
-  }
-
-  //
-  // moveFocusToDesktop() - move keyboard focus either to VncCanvas.
-  //
-
-  void moveFocusToDesktop() {
-    if (vncContainer != null) {
-      if (vc != null && vncContainer.isAncestorOf(vc))
-	vc.requestFocus();
-    }
-  }
-
-  //
-  // disconnect() - close connection to server.
-  //
-
-  synchronized public void disconnect() {
-    System.out.println("Disconnecting");
-
-    if (vc != null) {
-      double sec = (System.currentTimeMillis() - vc.statStartTime) / 1000.0;
-      double rate = Math.round(vc.statNumUpdates / sec * 100) / 100.0;
-      long nRealRects = vc.statNumPixelRects;
-      long nPseudoRects = vc.statNumTotalRects - vc.statNumPixelRects;
-      System.out.println("Updates received: " + vc.statNumUpdates + " (" +
-                         nRealRects + " rectangles + " + nPseudoRects +
-                         " pseudo), " + rate + " updates/sec");
-      long numRectsOther = nRealRects - vc.statNumRectsTight
-        - vc.statNumRectsZRLE - vc.statNumRectsHextile
-        - vc.statNumRectsRaw - vc.statNumRectsCopy;
-      System.out.println("Rectangles:" +
-                         " Tight=" + vc.statNumRectsTight +
-                         "(JPEG=" + vc.statNumRectsTightJPEG +
-                         ") ZRLE=" + vc.statNumRectsZRLE +
-                         " Hextile=" + vc.statNumRectsHextile +
-                         " Raw=" + vc.statNumRectsRaw +
-                         " CopyRect=" + vc.statNumRectsCopy +
-                         " other=" + numRectsOther);
-
-      long raw = vc.statNumBytesDecoded;
-      long compressed = vc.statNumBytesEncoded;
-      if (compressed > 0) {
-          double ratio = Math.round((double)raw / compressed * 1000) / 1000.0;
-          System.out.println("Pixel data: " + vc.statNumBytesDecoded +
-                             " bytes, " + vc.statNumBytesEncoded +
-                             " compressed, ratio " + ratio);
-      }
-    }
-
-    if (rfb != null && !rfb.closed())
-      rfb.close();
-    options.dispose();
-    clipboard.dispose();
-    if (rec != null)
-      rec.dispose();
-
-    if (inAnApplet) {
-      showMessage("Disconnected");
-    } else {
-      System.exit(0);
-    }
-  }
-
-  //
-  // fatalError() - print out a fatal error message.
-  // FIXME: Do we really need two versions of the fatalError() method?
-  //
-
-  synchronized public void fatalError(String str) {
-    System.out.println(str);
-
-    if (inAnApplet) {
-      // vncContainer null, applet not inited,
-      // can not present the error to the user.
-      Thread.currentThread().stop();
-    } else {
-      System.exit(1);
-    }
-  }
-
-  synchronized public void fatalError(String str, Exception e) {
- 
-    if (rfb != null && rfb.closed()) {
-      // Not necessary to show error message if the error was caused
-      // by I/O problems after the rfb.close() method call.
-      System.out.println("RFB thread finished");
-      return;
-    }
-
-    System.out.println(str);
-    e.printStackTrace();
-
-    if (rfb != null)
-      rfb.close();
-
-    if (inAnApplet) {
-      showMessage(str);
-    } else {
-      System.exit(1);
-    }
-  }
-
-  //
-  // Show message text and optionally "Relogin" and "Close" buttons.
-  //
-
-  void showMessage(String msg) {
-    vncContainer.removeAll();
-
-    Label errLabel = new Label(msg, Label.CENTER);
-    errLabel.setFont(new Font("Helvetica", Font.PLAIN, 12));
-
-    if (offerRelogin) {
-
-      Panel gridPanel = new Panel(new GridLayout(0, 1));
-      Panel outerPanel = new Panel(new FlowLayout(FlowLayout.LEFT));
-      outerPanel.add(gridPanel);
-      vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 16));
-      vncContainer.add(outerPanel);
-      Panel textPanel = new Panel(new FlowLayout(FlowLayout.CENTER));
-      textPanel.add(errLabel);
-      gridPanel.add(textPanel);
-      gridPanel.add(new ReloginPanel(this));
-
-    } else {
-
-      vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 30));
-      vncContainer.add(errLabel);
-
-    }
-
-    if (inSeparateFrame) {
-      vncFrame.pack();
-    } else {
-      validate();
-    }
-  }
-
-  //
-  // Stop the applet.
-  // Main applet thread will terminate on first exception
-  // after seeing that rfbThread has been set to null.
-  //
-
-  public void stop() {
-    System.out.println("Stopping applet");
-    rfbThread = null;
-  }
-
-  //
-  // This method is called before the applet is destroyed.
-  //
-
-  public void destroy() {
-    System.out.println("Destroying applet");
-
-    vncContainer.removeAll();
-    options.dispose();
-    clipboard.dispose();
-    if (rec != null)
-      rec.dispose();
-    if (rfb != null && !rfb.closed())
-      rfb.close();
-    if (inSeparateFrame)
-      vncFrame.dispose();
-  }
-
-  //
-  // Start/stop receiving mouse events.
-  //
-
-  public void enableInput(boolean enable) {
-    vc.enableInput(enable);
-  }
-  
-  //
-  // Resize framebuffer if autoScale is enabled.
-  //
-  
-  public void componentResized(ComponentEvent e) {
-    if (e.getComponent() == vncFrame) {
-      if (options.autoScale) {
-        if (vc != null) {
-          if (!vc.isFirstSizeAutoUpdate) {
-            vc.updateFramebufferSize();
+      if (argv[i].charAt(0) == '-') {
+        if (i+1 < argv.length) {
+          if (Configuration.setParam(argv[i].substring(1), argv[i+1])) {
+            i++;
+            continue;
           }
         }
+        usage();
       }
+
+      if (vncServerName.getValue() != null)
+        usage();
+      vncServerName.setParam(argv[i]);
     }
   }
-  
-  //
-  // Ignore component events we're not interested in.
-  //
-  
-  public void componentShown(ComponentEvent e) { }
-  public void componentMoved(ComponentEvent e) { }
-  public void componentHidden(ComponentEvent e) { }
 
-  //
-  // Close application properly on window close event.
-  //
+  public static void usage() {
+    String usage = ("\nusage: vncviewer [options/parameters] "+
+                    "[host:displayNum] [options/parameters]\n"+
+                    //"       vncviewer [options/parameters] -listen [port] "+
+                    //"[options/parameters]\n"+
+                    "\n"+
+                    "Options:\n"+
+                    "  -log <level>    configure logging level\n"+
+                    "\n"+
+                    "Parameters can be turned on with -<param> or off with "+
+                    "-<param>=0\n"+
+                    "Parameters which take a value can be specified as "+
+                    "-<param> <value>\n"+
+                    "Other valid forms are <param>=<value> -<param>=<value> "+
+                    "--<param>=<value>\n"+
+                    "Parameter names are case-insensitive.  The parameters "+
+                    "are:\n\n"+
+                    Configuration.listParams());
+    System.err.print(usage);
+    System.exit(1);
+  }
 
-  public void windowClosing(WindowEvent evt) {
-    System.out.println("Closing window");
-    if (rfb != null)
-      disconnect();
+  public VncViewer() {
+    applet = true;
+    firstApplet = true;
+  }
 
-    vncContainer.hide();
+  public static void newViewer(VncViewer oldViewer) {
+    VncViewer viewer = new VncViewer();
+    viewer.applet = oldViewer.applet;
+    viewer.firstApplet = false;
+    viewer.start();
+  }
 
-    if (!inAnApplet) {
+
+  public void init() {
+    vlog.debug("init called");
+    setBackground(Color.white);
+    logo = getImage(getDocumentBase(), "logo150x150.gif");
+  }
+
+  public void start() {
+    vlog.debug("start called");
+    nViewers++;
+    if (firstApplet) {
+      alwaysShowServerDialog.setParam(true);
+      Configuration.readAppletParams(this);
+      String host = getCodeBase().getHost();
+      if (vncServerName.getValue() == null && vncServerPort.getValue() != 0) {
+        int port = vncServerPort.getValue();
+        vncServerName.setParam(host + ((port >= 5900 && port <= 5999)
+                                       ? (":"+(port-5900))
+                                       : ("::"+port)));
+      }
+    }
+    thread = new Thread(this);
+    thread.start();
+  }
+
+  public void run() {
+    CConn cc = null;
+    try {
+      cc = new CConn(this, null, vncServerName.getValue(), false);
+      while (true)
+        cc.processMsg();
+    } catch (EndOfStream e) {
+      vlog.info(e.toString());
+    } catch (java.lang.Exception e) {
+      if (cc != null) cc.deleteWindow();
+      if (cc == null || !cc.shuttingDown) {
+        e.printStackTrace();
+        JOptionPane.showMessageDialog(null,
+          e.toString(),
+          "VNC Viewer : Error",
+          JOptionPane.ERROR_MESSAGE);
+      }
+    }
+    if (cc != null) cc.deleteWindow();
+    nViewers--;
+    if (!applet && nViewers == 0) {
       System.exit(0);
     }
   }
 
-  //
-  // Ignore window events we're not interested in.
-  //
+  BoolParameter fastCopyRect
+  = new BoolParameter("FastCopyRect",
+                          "Use fast CopyRect - turn this off if you get "+
+                          "screen corruption when copying from off-screen",
+                          true);
+  BoolParameter useLocalCursor
+  = new BoolParameter("UseLocalCursor",
+                          "Render the mouse cursor locally", true);
+  BoolParameter sendLocalUsername
+  = new BoolParameter("SendLocalUsername",
+                          "Send the local username for SecurityTypes "+
+                          "such as Plain rather than prompting", true);
+  BoolParameter autoSelect
+  = new BoolParameter("AutoSelect",
+                          "Auto select pixel format and encoding", true);
+  BoolParameter fullColour
+  = new BoolParameter("FullColour",
+                          "Use full colour - otherwise 6-bit colour is used "+
+                          "until AutoSelect decides the link is fast enough",
+                          true);
+  AliasParameter fullColor
+  = new AliasParameter("FullColor", "Alias for FullColour", fullColour);
+  StringParameter preferredEncoding
+  = new StringParameter("PreferredEncoding",
+                            "Preferred encoding to use (Tight, ZRLE, hextile or"+
+                            " raw) - implies AutoSelect=0", "Tight");
+  BoolParameter viewOnly
+  = new BoolParameter("ViewOnly", "Don't send any mouse or keyboard "+
+                          "events to the server", false);
+  BoolParameter shared
+  = new BoolParameter("Shared", "Don't disconnect other viewers upon "+
+                          "connection - share the desktop instead", false);
+  BoolParameter fullScreen
+  = new BoolParameter("FullScreen", "Full Screen Mode", false);
+  BoolParameter acceptClipboard
+  = new BoolParameter("AcceptClipboard",
+                          "Accept clipboard changes from the server", true);
+  BoolParameter sendClipboard
+  = new BoolParameter("SendClipboard",
+                          "Send clipboard changes to the server", true);
+  BoolParameter alwaysShowServerDialog
+  = new BoolParameter("AlwaysShowServerDialog",
+                          "Always show the server dialog even if a server "+
+                          "has been specified in an applet parameter or on "+
+                          "the command line", false);
+  StringParameter vncServerName
+  = new StringParameter("Server",
+                            "The VNC server <host>[:<dpyNum>] or "+
+                            "<host>::<port>", null);
+  IntParameter vncServerPort
+  = new IntParameter("Port",
+                         "The VNC server's port number, assuming it is on "+
+                         "the host from which the applet was downloaded", 0);
+  BoolParameter customCompressLevel
+  = new BoolParameter("CustomCompressLevel",
+                          "Use custom compression level. "+
+                          "Default if CompressLevel is specified.", false);
+  IntParameter compressLevel
+  = new IntParameter("CompressLevel",
+			                    "Use specified compression level "+
+			                    "0 = Low, 9 = High",
+			                    6);
+  BoolParameter noJpeg
+  = new BoolParameter("NoJPEG",
+                          "Disable lossy JPEG compression in Tight encoding.", false);
+  IntParameter qualityLevel
+  = new IntParameter("QualityLevel",
+			                    "JPEG quality level. "+
+			                    "0 = Low, 9 = High",
+			                    8);
 
-  public void windowActivated(WindowEvent evt) {}
-  public void windowDeactivated (WindowEvent evt) {}
-  public void windowOpened(WindowEvent evt) {}
-  public void windowClosed(WindowEvent evt) {}
-  public void windowIconified(WindowEvent evt) {}
-  public void windowDeiconified(WindowEvent evt) {}
+  Thread thread;
+  boolean applet, firstApplet;
+  Image logo;
+  static int nViewers;
+  static LogWriter vlog = new LogWriter("main");
 }
diff --git a/java/src/com/tigervnc/vncviewer/X509Tunnel.java b/java/src/com/tigervnc/vncviewer/X509Tunnel.java
deleted file mode 100644
index ddc3f82..0000000
--- a/java/src/com/tigervnc/vncviewer/X509Tunnel.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2003 Sun Microsystems, Inc.
- * Copyright (C) 2003-2010 Martin Koegler
- * Copyright (C) 2006 OCCAM Financial Technology
- *
- * 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.vncviewer;
-
-import java.util.*;
-import java.net.*;
-import javax.net.ssl.*;
-import java.security.*;
-import java.security.cert.*;
-
-public class X509Tunnel extends TLSTunnelBase
-{
-
-  public X509Tunnel (Socket sock_)
-  {
-    super (sock_);
-  }
-
-  protected void setParam (SSLSocket sock)
-  {
-    String[]supported;
-    ArrayList enabled = new ArrayList ();
-
-    supported = sock.getSupportedCipherSuites ();
-
-    for (int i = 0; i < supported.length; i++)
-      if (!supported[i].matches (".*DH_anon.*"))
-	enabled.add (supported[i]);
-
-    sock.setEnabledCipherSuites ((String[])enabled.toArray (new String[0]));
-  }
-
-  protected void initContext (SSLContext sc) throws java.security.
-    GeneralSecurityException
-  {
-    TrustManager[] myTM = new TrustManager[]
-    {
-    new MyX509TrustManager ()};
-    sc.init (null, myTM, null);
-  }
-
-
-  class MyX509TrustManager implements X509TrustManager
-  {
-
-    X509TrustManager tm;
-
-      MyX509TrustManager () throws java.security.GeneralSecurityException
-    {
-      TrustManagerFactory tmf =
-	TrustManagerFactory.getInstance ("SunX509", "SunJSSE");
-      KeyStore ks = KeyStore.getInstance ("JKS");
-        tmf.init (ks);
-        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)
-      {
-	MessageBox m =
-	  new MessageBox (e.toString (), MessageBox.MB_OKAYCANCEL);
-	if (!m.result ())
-	  throw e;
-      }
-    }
-
-    public X509Certificate[] getAcceptedIssuers ()
-    {
-      return tm.getAcceptedIssuers ();
-    }
-  }
-}
diff --git a/java/src/com/tigervnc/vncviewer/tigervnc.ico b/java/src/com/tigervnc/vncviewer/tigervnc.ico
new file mode 100644
index 0000000..50b90f0
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/tigervnc.ico
Binary files differ
diff --git a/java/src/com/tigervnc/vncviewer/tigervnc.png b/java/src/com/tigervnc/vncviewer/tigervnc.png
new file mode 100644
index 0000000..8ac883e
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/tigervnc.png
Binary files differ