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/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/DesCipher.java b/java/src/com/tigervnc/vncviewer/DesCipher.java
deleted file mode 100644
index 25362fe..0000000
--- a/java/src/com/tigervnc/vncviewer/DesCipher.java
+++ /dev/null
@@ -1,498 +0,0 @@
-//
-// This DES class has been extracted from package Acme.Crypto for use in VNC.
-// The bytebit[] array has been reversed so that the most significant bit
-// in each byte of the key is ignored, not the least significant.  Also the
-// unnecessary odd parity code has been removed.
-//
-// These changes are:
-//  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
-//
-// This software is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-//
-
-// DesCipher - the DES encryption method
-//
-// The meat of this code is by Dave Zimmerman <dzimm@widget.com>, and is:
-//
-// Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved.
-//
-// Permission to use, copy, modify, and distribute this software
-// and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
-// without fee is hereby granted, provided that this copyright notice is kept 
-// intact. 
-// 
-// WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
-// OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
-// TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
-// PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE
-// FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
-// DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
-// 
-// THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
-// CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
-// PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
-// NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
-// SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
-// SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
-// PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES").  WIDGET WORKSHOP
-// SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
-// HIGH RISK ACTIVITIES.
-//
-//
-// The rest is:
-//
-// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>.  All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-// 1. Redistributions of source code must retain the above copyright
-//    notice, this list of conditions and the following disclaimer.
-// 2. Redistributions in binary form must reproduce the above copyright
-//    notice, this list of conditions and the following disclaimer in the
-//    documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-// SUCH DAMAGE.
-//
-// Visit the ACME Labs Java page for up-to-date versions of this and other
-// fine Java utilities: http://www.acme.com/java/
-
-
-package com.tigervnc.vncviewer;
-
-import java.io.*;
-
-/// The DES encryption method.
-// <P>
-// This is surprisingly fast, for pure Java.  On a SPARC 20, wrapped
-// in Acme.Crypto.EncryptedOutputStream or Acme.Crypto.EncryptedInputStream,
-// it does around 7000 bytes/second.
-// <P>
-// Most of this code is by Dave Zimmerman <dzimm@widget.com>, and is
-// Copyright (c) 1996 Widget Workshop, Inc.  See the source file for details.
-// <P>
-// <A HREF="/resources/classes/Acme/Crypto/DesCipher.java">Fetch the software.</A><BR>
-// <A HREF="/resources/classes/Acme.tar.Z">Fetch the entire Acme package.</A>
-// <P>
-// @see Des3Cipher
-// @see EncryptedOutputStream
-// @see EncryptedInputStream
-
-public class DesCipher
-    {
-
-    // Constructor, byte-array key.
-    public DesCipher( byte[] key )
-	{
-	setKey( key );
-	}
-
-    // Key routines.
-
-    private int[] encryptKeys = new int[32];
-    private int[] decryptKeys = new int[32];
-
-    /// Set the key.
-    public void setKey( byte[] key )
-	{
-	deskey( key, true, encryptKeys );
-	deskey( key, false, decryptKeys );
-	}
-
-    // Turn an 8-byte key into internal keys.
-    private void deskey( byte[] keyBlock, boolean encrypting, int[] KnL )
-	{
-	int i, j, l, m, n;
-	int[] pc1m = new int[56];
-	int[] pcr = new int[56];
-	int[] kn = new int[32];
-
-	for ( j = 0; j < 56; ++j )
-	    {
-	    l = pc1[j];
-	    m = l & 07;
-	    pc1m[j] = ( (keyBlock[l >>> 3] & bytebit[m]) != 0 )? 1: 0;
-	    }
-
-	for ( i = 0; i < 16; ++i )
-	    {
-	    if ( encrypting )
-		m = i << 1;
-	    else
-		m = (15-i) << 1;
-	    n = m+1;
-	    kn[m] = kn[n] = 0;
-	    for ( j = 0; j < 28; ++j )
-		{
-		l = j+totrot[i];
-		if ( l < 28 )
-		    pcr[j] = pc1m[l];
-		else
-		    pcr[j] = pc1m[l-28];
-		}
-	    for ( j=28; j < 56; ++j )
-		{
-		l = j+totrot[i];
-		if ( l < 56 )
-		    pcr[j] = pc1m[l];
-		else
-		    pcr[j] = pc1m[l-28];
-		}
-	    for ( j = 0; j < 24; ++j )
-		{
-		if ( pcr[pc2[j]] != 0 )
-		    kn[m] |= bigbyte[j];
-		if ( pcr[pc2[j+24]] != 0 )
-		    kn[n] |= bigbyte[j];
-		}
-	    }
-	cookey( kn, KnL );
-	}
-
-    private void cookey( int[] raw, int KnL[] )
-	{
-	int raw0, raw1;
-	int rawi, KnLi;
-	int i;
-
-	for ( i = 0, rawi = 0, KnLi = 0; i < 16; ++i )
-	    {
-	    raw0 = raw[rawi++];
-	    raw1 = raw[rawi++];
-	    KnL[KnLi]  = (raw0 & 0x00fc0000) <<   6;
-	    KnL[KnLi] |= (raw0 & 0x00000fc0) <<  10;
-	    KnL[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
-	    KnL[KnLi] |= (raw1 & 0x00000fc0) >>>  6;
-	    ++KnLi;
-	    KnL[KnLi]  = (raw0 & 0x0003f000) <<  12;
-	    KnL[KnLi] |= (raw0 & 0x0000003f) <<  16;
-	    KnL[KnLi] |= (raw1 & 0x0003f000) >>>  4;
-	    KnL[KnLi] |= (raw1 & 0x0000003f);
-	    ++KnLi;
-	    }
-	}
-
-
-    // Block encryption routines.
-
-    private int[] tempInts = new int[2];
-
-    /// Encrypt a block of eight bytes.
-    public void encrypt( byte[] clearText, int clearOff, byte[] cipherText, int cipherOff )
-	{
-	squashBytesToInts( clearText, clearOff, tempInts, 0, 2 );
-	des( tempInts, tempInts, encryptKeys );
-	spreadIntsToBytes( tempInts, 0, cipherText, cipherOff, 2 );
-	}
-
-    /// Decrypt a block of eight bytes.
-    public void decrypt( byte[] cipherText, int cipherOff, byte[] clearText, int clearOff )
-	{
-	squashBytesToInts( cipherText, cipherOff, tempInts, 0, 2 );
-	des( tempInts, tempInts, decryptKeys );
-	spreadIntsToBytes( tempInts, 0, clearText, clearOff, 2 );
-	}
-
-    // The DES function.
-    private void des( int[] inInts, int[] outInts, int[] keys )
-	{
-	int fval, work, right, leftt;
-	int round;
-	int keysi = 0;
-
-	leftt = inInts[0];
-	right = inInts[1];
-
-	work   = ((leftt >>>  4) ^ right) & 0x0f0f0f0f;
-	right ^= work;
-	leftt ^= (work << 4);
-
-	work   = ((leftt >>> 16) ^ right) & 0x0000ffff;
-	right ^= work;
-	leftt ^= (work << 16);
-
-	work   = ((right >>>  2) ^ leftt) & 0x33333333;
-	leftt ^= work;
-	right ^= (work << 2);
-
-	work   = ((right >>>  8) ^ leftt) & 0x00ff00ff;
-	leftt ^= work;
-	right ^= (work << 8);
-	right  = (right << 1) | ((right >>> 31) & 1);
-
-	work   = (leftt ^ right) & 0xaaaaaaaa;
-	leftt ^= work;
-	right ^= work;
-	leftt  = (leftt << 1) | ((leftt >>> 31) & 1);
-
-	for ( round = 0; round < 8; ++round )
-	    {
-	    work   = (right << 28) | (right >>> 4);
-	    work  ^= keys[keysi++];
-	    fval   = SP7[ work	       & 0x0000003f ];
-	    fval  |= SP5[(work >>>  8) & 0x0000003f ];
-	    fval  |= SP3[(work >>> 16) & 0x0000003f ];
-	    fval  |= SP1[(work >>> 24) & 0x0000003f ];
-	    work   = right ^ keys[keysi++];
-	    fval  |= SP8[ work         & 0x0000003f ];
-	    fval  |= SP6[(work >>>  8) & 0x0000003f ];
-	    fval  |= SP4[(work >>> 16) & 0x0000003f ];
-	    fval  |= SP2[(work >>> 24) & 0x0000003f ];
-	    leftt ^= fval;
-	    work   = (leftt << 28) | (leftt >>> 4);
-	    work  ^= keys[keysi++];
-	    fval   = SP7[ work	       & 0x0000003f ];
-	    fval  |= SP5[(work >>>  8) & 0x0000003f ];
-	    fval  |= SP3[(work >>> 16) & 0x0000003f ];
-	    fval  |= SP1[(work >>> 24) & 0x0000003f ];
-	    work   = leftt ^ keys[keysi++];
-	    fval  |= SP8[ work	       & 0x0000003f ];
-	    fval  |= SP6[(work >>>  8) & 0x0000003f ];
-	    fval  |= SP4[(work >>> 16) & 0x0000003f ];
-	    fval  |= SP2[(work >>> 24) & 0x0000003f ];
-	    right ^= fval;
-	    }
-
-	right  = (right << 31) | (right >>> 1);
-	work   = (leftt ^ right) & 0xaaaaaaaa;
-	leftt ^= work;
-	right ^= work;
-	leftt  = (leftt << 31) | (leftt >>> 1);
-	work   = ((leftt >>>  8) ^ right) & 0x00ff00ff;
-	right ^= work;
-	leftt ^= (work << 8);
-	work   = ((leftt >>>  2) ^ right) & 0x33333333;
-	right ^= work;
-	leftt ^= (work << 2);
-	work   = ((right >>> 16) ^ leftt) & 0x0000ffff;
-	leftt ^= work;
-	right ^= (work << 16);
-	work   = ((right >>>  4) ^ leftt) & 0x0f0f0f0f;
-	leftt ^= work;
-	right ^= (work << 4);
-	outInts[0] = right;
-	outInts[1] = leftt;
-	}
-
-
-    // Tables, permutations, S-boxes, etc.
-
-    private static byte[] bytebit = {
-	(byte)0x01, (byte)0x02, (byte)0x04, (byte)0x08,
-	(byte)0x10, (byte)0x20, (byte)0x40, (byte)0x80
-	};
-    private static int[] bigbyte = {
-	0x800000, 0x400000, 0x200000, 0x100000,
-	0x080000, 0x040000, 0x020000, 0x010000,
-	0x008000, 0x004000, 0x002000, 0x001000,
-	0x000800, 0x000400, 0x000200, 0x000100,
-	0x000080, 0x000040, 0x000020, 0x000010,
-	0x000008, 0x000004, 0x000002, 0x000001
-	};
-    private static byte[] pc1 = {
-         (byte)56, (byte)48, (byte)40, (byte)32, (byte)24, (byte)16, (byte) 8,
-      (byte) 0, (byte)57, (byte)49, (byte)41, (byte)33, (byte)25, (byte)17,
-	 (byte) 9, (byte) 1, (byte)58, (byte)50, (byte)42, (byte)34, (byte)26,
-      (byte)18, (byte)10, (byte) 2, (byte)59, (byte)51, (byte)43, (byte)35,
-	 (byte)62, (byte)54, (byte)46, (byte)38, (byte)30, (byte)22, (byte)14,
-      (byte) 6, (byte)61, (byte)53, (byte)45, (byte)37, (byte)29, (byte)21,
-	 (byte)13, (byte) 5, (byte)60, (byte)52, (byte)44, (byte)36, (byte)28,
-      (byte)20, (byte)12, (byte) 4, (byte)27, (byte)19, (byte)11, (byte)3
-	};
-    private static int[] totrot = {
-        1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28
-	};
-
-    private static byte[] pc2 = {
-	(byte)13, (byte)16, (byte)10, (byte)23, (byte) 0, (byte) 4,
-	          (byte) 2, (byte)27, (byte)14, (byte) 5, (byte)20, (byte) 9,
-	(byte)22, (byte)18, (byte)11, (byte)3 , (byte)25, (byte) 7,
-	          (byte)15, (byte) 6, (byte)26, (byte)19, (byte)12, (byte) 1,
-	(byte)40, (byte)51, (byte)30, (byte)36, (byte)46, (byte)54,
-	          (byte)29, (byte)39, (byte)50, (byte)44, (byte)32, (byte)47,
-	(byte)43, (byte)48, (byte)38, (byte)55, (byte)33, (byte)52,
-	          (byte)45, (byte)41, (byte)49, (byte)35, (byte)28, (byte)31,
-	};
-
-    private static int[] SP1 = {
-        0x01010400, 0x00000000, 0x00010000, 0x01010404,
-	0x01010004, 0x00010404, 0x00000004, 0x00010000,
-	0x00000400, 0x01010400, 0x01010404, 0x00000400,
-	0x01000404, 0x01010004, 0x01000000, 0x00000004,
-	0x00000404, 0x01000400, 0x01000400, 0x00010400,
-	0x00010400, 0x01010000, 0x01010000, 0x01000404,
-	0x00010004, 0x01000004, 0x01000004, 0x00010004,
-	0x00000000, 0x00000404, 0x00010404, 0x01000000,
-	0x00010000, 0x01010404, 0x00000004, 0x01010000,
-	0x01010400, 0x01000000, 0x01000000, 0x00000400,
-	0x01010004, 0x00010000, 0x00010400, 0x01000004,
-	0x00000400, 0x00000004, 0x01000404, 0x00010404,
-	0x01010404, 0x00010004, 0x01010000, 0x01000404,
-	0x01000004, 0x00000404, 0x00010404, 0x01010400,
-	0x00000404, 0x01000400, 0x01000400, 0x00000000,
-	0x00010004, 0x00010400, 0x00000000, 0x01010004
-	};
-    private static int[] SP2 = {
-	0x80108020, 0x80008000, 0x00008000, 0x00108020,
-	0x00100000, 0x00000020, 0x80100020, 0x80008020,
-	0x80000020, 0x80108020, 0x80108000, 0x80000000,
-	0x80008000, 0x00100000, 0x00000020, 0x80100020,
-	0x00108000, 0x00100020, 0x80008020, 0x00000000,
-	0x80000000, 0x00008000, 0x00108020, 0x80100000,
-	0x00100020, 0x80000020, 0x00000000, 0x00108000,
-	0x00008020, 0x80108000, 0x80100000, 0x00008020,
-	0x00000000, 0x00108020, 0x80100020, 0x00100000,
-	0x80008020, 0x80100000, 0x80108000, 0x00008000,
-	0x80100000, 0x80008000, 0x00000020, 0x80108020,
-	0x00108020, 0x00000020, 0x00008000, 0x80000000,
-	0x00008020, 0x80108000, 0x00100000, 0x80000020,
-	0x00100020, 0x80008020, 0x80000020, 0x00100020,
-	0x00108000, 0x00000000, 0x80008000, 0x00008020,
-	0x80000000, 0x80100020, 0x80108020, 0x00108000
-	};
-    private static int[] SP3 = {
-	0x00000208, 0x08020200, 0x00000000, 0x08020008,
-	0x08000200, 0x00000000, 0x00020208, 0x08000200,
-	0x00020008, 0x08000008, 0x08000008, 0x00020000,
-	0x08020208, 0x00020008, 0x08020000, 0x00000208,
-	0x08000000, 0x00000008, 0x08020200, 0x00000200,
-	0x00020200, 0x08020000, 0x08020008, 0x00020208,
-	0x08000208, 0x00020200, 0x00020000, 0x08000208,
-	0x00000008, 0x08020208, 0x00000200, 0x08000000,
-	0x08020200, 0x08000000, 0x00020008, 0x00000208,
-	0x00020000, 0x08020200, 0x08000200, 0x00000000,
-	0x00000200, 0x00020008, 0x08020208, 0x08000200,
-	0x08000008, 0x00000200, 0x00000000, 0x08020008,
-	0x08000208, 0x00020000, 0x08000000, 0x08020208,
-	0x00000008, 0x00020208, 0x00020200, 0x08000008,
-	0x08020000, 0x08000208, 0x00000208, 0x08020000,
-	0x00020208, 0x00000008, 0x08020008, 0x00020200
-	};
-    private static int[] SP4 = {
-	0x00802001, 0x00002081, 0x00002081, 0x00000080,
-	0x00802080, 0x00800081, 0x00800001, 0x00002001,
-	0x00000000, 0x00802000, 0x00802000, 0x00802081,
-	0x00000081, 0x00000000, 0x00800080, 0x00800001,
-	0x00000001, 0x00002000, 0x00800000, 0x00802001,
-	0x00000080, 0x00800000, 0x00002001, 0x00002080,
-	0x00800081, 0x00000001, 0x00002080, 0x00800080,
-	0x00002000, 0x00802080, 0x00802081, 0x00000081,
-	0x00800080, 0x00800001, 0x00802000, 0x00802081,
-	0x00000081, 0x00000000, 0x00000000, 0x00802000,
-	0x00002080, 0x00800080, 0x00800081, 0x00000001,
-	0x00802001, 0x00002081, 0x00002081, 0x00000080,
-	0x00802081, 0x00000081, 0x00000001, 0x00002000,
-	0x00800001, 0x00002001, 0x00802080, 0x00800081,
-	0x00002001, 0x00002080, 0x00800000, 0x00802001,
-	0x00000080, 0x00800000, 0x00002000, 0x00802080
-	};
-    private static int[] SP5 = {
-	0x00000100, 0x02080100, 0x02080000, 0x42000100,
-	0x00080000, 0x00000100, 0x40000000, 0x02080000,
-	0x40080100, 0x00080000, 0x02000100, 0x40080100,
-	0x42000100, 0x42080000, 0x00080100, 0x40000000,
-	0x02000000, 0x40080000, 0x40080000, 0x00000000,
-	0x40000100, 0x42080100, 0x42080100, 0x02000100,
-	0x42080000, 0x40000100, 0x00000000, 0x42000000,
-	0x02080100, 0x02000000, 0x42000000, 0x00080100,
-	0x00080000, 0x42000100, 0x00000100, 0x02000000,
-	0x40000000, 0x02080000, 0x42000100, 0x40080100,
-	0x02000100, 0x40000000, 0x42080000, 0x02080100,
-	0x40080100, 0x00000100, 0x02000000, 0x42080000,
-	0x42080100, 0x00080100, 0x42000000, 0x42080100,
-	0x02080000, 0x00000000, 0x40080000, 0x42000000,
-	0x00080100, 0x02000100, 0x40000100, 0x00080000,
-	0x00000000, 0x40080000, 0x02080100, 0x40000100
-	};
-    private static int[] SP6 = {
-	0x20000010, 0x20400000, 0x00004000, 0x20404010,
-	0x20400000, 0x00000010, 0x20404010, 0x00400000,
-	0x20004000, 0x00404010, 0x00400000, 0x20000010,
-	0x00400010, 0x20004000, 0x20000000, 0x00004010,
-	0x00000000, 0x00400010, 0x20004010, 0x00004000,
-	0x00404000, 0x20004010, 0x00000010, 0x20400010,
-	0x20400010, 0x00000000, 0x00404010, 0x20404000,
-	0x00004010, 0x00404000, 0x20404000, 0x20000000,
-	0x20004000, 0x00000010, 0x20400010, 0x00404000,
-	0x20404010, 0x00400000, 0x00004010, 0x20000010,
-	0x00400000, 0x20004000, 0x20000000, 0x00004010,
-	0x20000010, 0x20404010, 0x00404000, 0x20400000,
-	0x00404010, 0x20404000, 0x00000000, 0x20400010,
-	0x00000010, 0x00004000, 0x20400000, 0x00404010,
-	0x00004000, 0x00400010, 0x20004010, 0x00000000,
-	0x20404000, 0x20000000, 0x00400010, 0x20004010
-	};
-    private static int[] SP7 = {
-	0x00200000, 0x04200002, 0x04000802, 0x00000000,
-	0x00000800, 0x04000802, 0x00200802, 0x04200800,
-	0x04200802, 0x00200000, 0x00000000, 0x04000002,
-	0x00000002, 0x04000000, 0x04200002, 0x00000802,
-	0x04000800, 0x00200802, 0x00200002, 0x04000800,
-	0x04000002, 0x04200000, 0x04200800, 0x00200002,
-	0x04200000, 0x00000800, 0x00000802, 0x04200802,
-	0x00200800, 0x00000002, 0x04000000, 0x00200800,
-	0x04000000, 0x00200800, 0x00200000, 0x04000802,
-	0x04000802, 0x04200002, 0x04200002, 0x00000002,
-	0x00200002, 0x04000000, 0x04000800, 0x00200000,
-	0x04200800, 0x00000802, 0x00200802, 0x04200800,
-	0x00000802, 0x04000002, 0x04200802, 0x04200000,
-	0x00200800, 0x00000000, 0x00000002, 0x04200802,
-	0x00000000, 0x00200802, 0x04200000, 0x00000800,
-	0x04000002, 0x04000800, 0x00000800, 0x00200002
-	};
-    private static int[] SP8 = {
-	0x10001040, 0x00001000, 0x00040000, 0x10041040,
-	0x10000000, 0x10001040, 0x00000040, 0x10000000,
-	0x00040040, 0x10040000, 0x10041040, 0x00041000,
-	0x10041000, 0x00041040, 0x00001000, 0x00000040,
-	0x10040000, 0x10000040, 0x10001000, 0x00001040,
-	0x00041000, 0x00040040, 0x10040040, 0x10041000,
-	0x00001040, 0x00000000, 0x00000000, 0x10040040,
-	0x10000040, 0x10001000, 0x00041040, 0x00040000,
-	0x00041040, 0x00040000, 0x10041000, 0x00001000,
-	0x00000040, 0x10040040, 0x00001000, 0x00041040,
-	0x10001000, 0x00000040, 0x10000040, 0x10040000,
-	0x10040040, 0x10000000, 0x00040000, 0x10001040,
-	0x00000000, 0x10041040, 0x00040040, 0x10000040,
-	0x10040000, 0x10001000, 0x10001040, 0x00000000,
-	0x10041040, 0x00041000, 0x00041000, 0x00001040,
-	0x00001040, 0x00040040, 0x10000000, 0x10041000
-	};
-
-    // Routines taken from other parts of the Acme utilities.
-
-    /// Squash bytes down to ints.
-    public static void squashBytesToInts( byte[] inBytes, int inOff, int[] outInts, int outOff, int intLen )
-        {
-	for ( int i = 0; i < intLen; ++i )
-	    outInts[outOff + i] = 
-		( ( inBytes[inOff + i * 4    ] & 0xff ) << 24 ) |
-		( ( inBytes[inOff + i * 4 + 1] & 0xff ) << 16 ) |
-		( ( inBytes[inOff + i * 4 + 2] & 0xff ) <<  8 ) |
-		  ( inBytes[inOff + i * 4 + 3] & 0xff );
-        }
-
-    /// Spread ints into bytes.
-    public static void spreadIntsToBytes( int[] inInts, int inOff, byte[] outBytes, int outOff, int intLen )
-        {
-	for ( int i = 0; i < intLen; ++i )
-	    {
-	    outBytes[outOff + i * 4    ] = (byte) ( inInts[inOff + i] >>> 24 );
-	    outBytes[outOff + i * 4 + 1] = (byte) ( inInts[inOff + i] >>> 16 );
-	    outBytes[outOff + i * 4 + 2] = (byte) ( inInts[inOff + i] >>>  8 );
-	    outBytes[outOff + i * 4 + 3] = (byte)   inInts[inOff + i];
-	    }
-        }
-    }
diff --git a/java/src/com/tigervnc/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%
rename from java/src/com/tigervnc/vncviewer/MemInStream.java
rename 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/ZlibInStream.java b/java/src/com/tigervnc/vncviewer/ZlibInStream.java
deleted file mode 100644
index 2f2a3c0..0000000
--- a/java/src/com/tigervnc/vncviewer/ZlibInStream.java
+++ /dev/null
@@ -1,113 +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.
- */
-
-//
-// A ZlibInStream reads from a zlib.io.InputStream
-//
-
-package com.tigervnc.vncviewer;
-
-public class ZlibInStream extends InStream {
-
-  static final int defaultBufSize = 16384;
-
-  public ZlibInStream(int bufSize_) {
-    bufSize = bufSize_;
-    b = new byte[bufSize];
-    ptr = end = ptrOffset = 0;
-    inflater = new java.util.zip.Inflater();
-  }
-
-  public ZlibInStream() { this(defaultBufSize); }
-
-  public void setUnderlying(InStream is, int bytesIn_) {
-    underlying = is;
-    bytesIn = bytesIn_;
-    ptr = end = 0;
-  }
-
-  public void reset() throws Exception {
-    ptr = end = 0;
-    if (underlying == null) return;
-
-    while (bytesIn > 0) {
-      decompress();
-      end = 0; // throw away any data
-    }
-    underlying = null;
-  }
-
-  public int pos() { return ptrOffset + ptr; }
-
-  protected int overrun(int itemSize, int nItems) throws Exception {
-    if (itemSize > bufSize)
-      throw new Exception("ZlibInStream overrun: max itemSize exceeded");
-    if (underlying == null)
-      throw new Exception("ZlibInStream overrun: no underlying stream");
-
-    if (end - ptr != 0)
-      System.arraycopy(b, ptr, b, 0, end - ptr);
-
-    ptrOffset += ptr;
-    end -= ptr;
-    ptr = 0;
-
-    while (end < itemSize) {
-      decompress();
-    }
-
-    if (itemSize * nItems > end)
-      nItems = end / itemSize;
-
-    return nItems;
-  }
-
-  // decompress() calls the decompressor once.  Note that this won't
-  // necessarily generate any output data - it may just consume some input
-  // data.  Returns false if wait is false and we would block on the underlying
-  // stream.
-
-  private void decompress() throws Exception {
-    try {
-      underlying.check(1);
-      int avail_in = underlying.getend() - underlying.getptr();
-      if (avail_in > bytesIn)
-        avail_in = bytesIn;
-
-      if (inflater.needsInput()) {
-        inflater.setInput(underlying.getbuf(), underlying.getptr(), avail_in);
-      }
-
-      int n = inflater.inflate(b, end, bufSize - end); 
-
-      end += n;
-      if (inflater.needsInput()) {
-        bytesIn -= avail_in;
-        underlying.setptr(underlying.getptr() + avail_in);
-      }
-    } catch (java.util.zip.DataFormatException e) {
-      throw new Exception("ZlibInStream: inflate failed");
-    }
-  }
-
-  private InStream underlying;
-  private int bufSize;
-  private int ptrOffset;
-  private java.util.zip.Inflater inflater;
-  private int bytesIn;
-}
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