changeset 24:87b29d6039a6

add package myVncProxy
author e085711
date Sun, 24 Apr 2011 23:03:00 +0900
parents b51bb7bc0766
children cded9fd297ab
files .settings/org.eclipse.core.resources.prefs Makefile/MANIFEST.MF src/AuthPanel.java src/ButtonPanel.java src/CapabilityInfo.java src/CapsContainer.java src/ClipboardFrame.java src/DesCipher.java src/HTTPConnectSocket.java src/HTTPConnectSocketFactory.java src/InStream.java src/MemInStream.java src/MyRfbProto.java src/OptionsFrame.java src/ProxyVncCanvas.java src/RecordingFrame.java src/ReloginPanel.java src/RfbProto.java src/SessionRecorder.java src/SocketFactory.java src/VncCanvas.java src/VncCanvas2.java src/VncProxyService.java src/VncViewer.java src/ZlibInStream.java src/acceptThread.java src/myVncProxy/AuthPanel.java src/myVncProxy/ButtonPanel.java src/myVncProxy/CapabilityInfo.java src/myVncProxy/CapsContainer.java src/myVncProxy/ClipboardFrame.java src/myVncProxy/DesCipher.java src/myVncProxy/HTTPConnectSocket.java src/myVncProxy/HTTPConnectSocketFactory.java src/myVncProxy/InStream.java src/myVncProxy/MemInStream.java src/myVncProxy/MyRfbProto.java src/myVncProxy/OptionsFrame.java src/myVncProxy/ProxyVncCanvas.java src/myVncProxy/RecordingFrame.java src/myVncProxy/ReloginPanel.java src/myVncProxy/RfbProto.java src/myVncProxy/SessionRecorder.java src/myVncProxy/SocketFactory.java src/myVncProxy/VncCanvas.java src/myVncProxy/VncCanvas2.java src/myVncProxy/VncProxyService.java src/myVncProxy/VncViewer.java src/myVncProxy/ZlibInStream.java src/myVncProxy/acceptThread.java
diffstat 50 files changed, 9866 insertions(+), 9839 deletions(-) [+]
line wrap: on
line diff
--- a/.settings/org.eclipse.core.resources.prefs	Sun Apr 24 16:48:39 2011 +0900
+++ b/.settings/org.eclipse.core.resources.prefs	Sun Apr 24 23:03:00 2011 +0900
@@ -1,3 +1,3 @@
-#Tue Apr 12 14:58:33 JST 2011
+#Sun Apr 24 16:49:46 JST 2011
 eclipse.preferences.version=1
-encoding//src/VncCanvas.java=UTF-8
+encoding//src/myVncProxy/VncCanvas.java=UTF-8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile/MANIFEST.MF	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+Main-Class: VncViewer
--- a/src/AuthPanel.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,116 +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.
-//
-
-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;
-
-  //
-  // Constructor.
-  //
-
-  public AuthPanel(VncViewer viewer)
-  {
-    Label titleLabel = new Label("VNC Authentication", Label.CENTER);
-    titleLabel.setFont(new Font("Helvetica", Font.BOLD, 18));
-
-    Label promptLabel = new Label("Password:", Label.CENTER);
-
-    passwordField = new TextField(10);
-    passwordField.setForeground(Color.black);
-    passwordField.setBackground(Color.white);
-    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();
-  }
-
-}
--- a/src/ButtonPanel.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -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.
-//
-
-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);
-
-    validate();
-  }
-
-  //
-  // 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();
-      }
-    }
-  }
-}
-
--- a/src/CapabilityInfo.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-//
-//  Copyright (C) 2003 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.
-//
-
-//
-// CapabilityInfo.java - A class to hold information about a
-// particular capability as used in the RFB protocol 3.130.
-//
-
-class CapabilityInfo {
-
-  // Public methods
-
-  public CapabilityInfo(int code,
-			String vendorSignature,
-			String nameSignature,
-			String description) {
-    this.code = code;
-    this.vendorSignature = vendorSignature;
-    this.nameSignature = nameSignature;
-    this.description = description;
-    enabled = false;
-  }
-
-  public CapabilityInfo(int code,
-			byte[] vendorSignature,
-			byte[] nameSignature) {
-    this.code = code;
-    this.vendorSignature = new String(vendorSignature);
-    this.nameSignature = new String(nameSignature);
-    this.description = null;
-    enabled = false;
-  }
-
-  public int getCode() {
-    return code;
-  }
-
-  public String getDescription() {
-    return description;
-  }
-
-  public boolean isEnabled() {
-    return enabled;
-  }
-
-  public void enable() {
-    enabled = true;
-  }
-
-  public boolean equals(CapabilityInfo other) {
-    return (other != null && this.code == other.code &&
-	    this.vendorSignature.equals(other.vendorSignature) &&
-	    this.nameSignature.equals(other.nameSignature));
-  }
-
-  public boolean enableIfEquals(CapabilityInfo other) {
-    if (this.equals(other))
-      enable();
-
-    return isEnabled();
-  }
-
-  // Protected data
-
-  protected int code;
-  protected String vendorSignature;
-  protected String nameSignature;
-
-  protected String description;
-  protected boolean enabled;
-}
--- a/src/CapsContainer.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-//
-//  Copyright (C) 2003 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.
-//
-
-//
-// CapsContainer.java - A container of capabilities as used in the RFB
-// protocol 3.130
-//
-
-import java.util.Vector;
-import java.util.Hashtable;
-
-class CapsContainer {
-
-  // Public methods
-
-  public CapsContainer() {
-    infoMap = new Hashtable(64, (float)0.25);
-    orderedList = new Vector(32, 8);
-  }
-
-  public void add(CapabilityInfo capinfo) {
-    Integer key = new Integer(capinfo.getCode());
-    infoMap.put(key, capinfo);
-  }
-
-  public void add(int code, String vendor, String name, String desc) {
-    Integer key = new Integer(code);
-    infoMap.put(key, new CapabilityInfo(code, vendor, name, desc));
-  }
-
-  public boolean isKnown(int code) {
-    return infoMap.containsKey(new Integer(code));
-  }
-
-  public CapabilityInfo getInfo(int code) {
-    return (CapabilityInfo)infoMap.get(new Integer(code));
-  }
-
-  public String getDescription(int code) {
-    CapabilityInfo capinfo = (CapabilityInfo)infoMap.get(new Integer(code));
-    if (capinfo == null)
-      return null;
-
-    return capinfo.getDescription();
-  }
-
-  public boolean enable(CapabilityInfo other) {
-    Integer key = new Integer(other.getCode());
-    CapabilityInfo capinfo = (CapabilityInfo)infoMap.get(key);
-    if (capinfo == null)
-      return false;
-
-    boolean enabled = capinfo.enableIfEquals(other);
-    if (enabled)
-      orderedList.addElement(key);
-
-    return enabled;
-  }
-
-  public boolean isEnabled(int code) {
-    CapabilityInfo capinfo = (CapabilityInfo)infoMap.get(new Integer(code));
-    if (capinfo == null)
-      return false;
-
-    return capinfo.isEnabled();
-  }
-
-  public int numEnabled() {
-    return orderedList.size();
-  }
-
-  public int getByOrder(int idx) {
-    int code;
-    try {
-      code = ((Integer)orderedList.elementAt(idx)).intValue();
-    } catch (ArrayIndexOutOfBoundsException e) {
-      code = 0;
-    }
-    return code;
-  }
-
-  // Protected data
-
-  protected Hashtable infoMap;
-  protected Vector orderedList;
-}
-
--- a/src/ClipboardFrame.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +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.
-//
-
-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("TightVNC 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);
-    }
-  }
-}
--- a/src/DesCipher.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,496 +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/
-
-
-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];
-	    }
-        }
-    }
--- a/src/HTTPConnectSocket.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +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.
-//
-
-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);
-  }
-}
-
--- a/src/HTTPConnectSocketFactory.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +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.
-//
-
-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;
-  }
-}
-
--- a/src/InStream.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,177 +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).
-//
-
-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;
-}
--- a/src/MemInStream.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +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.
- */
-
-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");
-  }
-}
--- a/src/MyRfbProto.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,205 +0,0 @@
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.BindException;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.util.LinkedList;
-
-
-class MyRfbProto extends RfbProto {
-
-	private int messageType;
-	private int rectangles;
-	private int rectX;
-	private int rectY;
-	private int rectW;
-	private int rectH;
-	private int encoding;
-
-	private ServerSocket servSock;
-	private int acceptPort;
-	private byte initData[];
-	private LinkedList <Socket> cliList;
-	
-	MyRfbProto(String h, int p, VncViewer v ) throws IOException {
-		super(h, p, v);
-		cliList = new LinkedList <Socket>();
-	}
-
-	MyRfbProto(String h, int p) throws IOException {
-		super(h, p);
-		cliList = new LinkedList <Socket>();
-	}
-	
-	void initServSock(int port) throws IOException{
-		servSock = new ServerSocket(port);
-		acceptPort = port;
-	}
-	void selectPort(){
-		int i = 5550;
-		while(true){
-			try{
-				initServSock(i);
-				break;
-			}catch(BindException e){
-				i++;
-				continue;
-			}catch(IOException e){
-
-			}
-		}
-	}
-	int getAcceptPort(){
-		return acceptPort;
-	}
-	void setSoTimeout(int num) throws IOException {
-		servSock.setSoTimeout(num);
-	}
-	
-	Socket accept() throws IOException {
-		return servSock.accept();
-	}
-
-	void addSock(Socket sock){
-		cliList.add(sock);
-	}
-	
-	void mark(int len) throws IOException {
-		is.mark(len);
-	}
-
-	void reset() throws IOException {
-		is.reset();
- 	}
-
-	boolean markSupported() {
-		return is.markSupported();
-	}
-	
-	void readServerInit() throws IOException {
-		
-		mark(255);
-		skipBytes(20);
-		int nlen = readU32();
-		int blen = 20+4+nlen;
-		initData = new byte[blen];
-		reset();
-
-		mark(blen);
-		readFully(initData);
-		reset();
-		
-		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);
-
-		// Read interaction capabilities (TightVNC protocol extensions)
-		if (protocolTightVNC) {
-			int nServerMessageTypes = readU16();
-			int nClientMessageTypes = readU16();
-			int nEncodingTypes = readU16();
-			readU16();
-			readCapabilityList(serverMsgCaps, nServerMessageTypes);
-			readCapabilityList(clientMsgCaps, nClientMessageTypes);
-			readCapabilityList(encodingCaps, nEncodingTypes);
-		}
-
-		inNormalProtocol = true;
-	}
-
-	void sendInitData(Socket sock) throws IOException{
-			sock.getOutputStream().write(initData);
-	}
-
-//	void sendData(byte b[]) throws IOException{
-	void sendData(byte b[]){
-		try{
-			for(Socket cli : cliList){
-				try{
-					cli.getOutputStream().write(b, 0, b.length);
-				}catch(IOException e){
-					// if socket closed
-					//				cliList.remove(cli);
-					cliList.remove(cli);
-				}
-			}
-//		System.out.println("cliSize="+cliSize());
-		}catch(Exception e){
-//			System.out.println("cliSize 0");
-		}
-	}	
-	boolean ready() throws IOException {
-		BufferedReader br = new BufferedReader(new InputStreamReader(is));
-		return br.ready();
-	}	
-
-	int cliSize(){
-		return cliList.size();
-	}	
-	void printNumBytesRead(){
-		System.out.println("numBytesRead="+numBytesRead);
-	}	
-	void bufResetSend(int size) throws IOException {
-		reset();
-		int len = size;
-		if(available() < size )
-			len = available();
-		byte buffer[] = new byte[len];
-		readFully(buffer);
-		sendData(buffer);
-	}
-	void regiFramebufferUpdate()throws IOException{
-		mark(16);
-		messageType = readU8();
-		skipBytes(1);
-		rectangles = readU16();
-		rectX = readU16();
-		rectY = readU16();
-		rectW = readU16();
-		rectH = readU16();
-		encoding = readU32();	
-		reset();
-	}
-	void checkAndMark() throws IOException{
-		switch(encoding){
-		case RfbProto.EncodingRaw:
-			mark(rectW * rectH * 4 + 16);		
-			break;
-		default:
-			mark(1000000);
-		}
-	}
-	
-	void printFramebufferUpdate(){
-	
-		System.out.println("messageType=" + messageType);
-		System.out.println("rectangles="+rectangles);
-		System.out.println("encoding=" + encoding);
-		switch(encoding){
-		case RfbProto.EncodingRaw:
-			System.out.println("rectW * rectH * 4 + 16 =" + rectW * rectH * 4 + 16);
-			break;
-		default:
-			
-		
-		}
-	}
-	
-	
-}
--- a/src/OptionsFrame.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,405 +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.
-//
-
-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",
-    "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" },
-    { "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,
-    scaleCursorIndex     = 8,
-    shareDesktopIndex    = 9;
-
-  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("TightVNC 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);
-	  }
-	}
-      }
-    }
-
-    // FIXME: Provide some sort of GUI for "Scaling Factor".
-
-    autoScale = false;
-    scalingFactor = 100;
-    String s = viewer.readParameter("Scaling Factor", false);
-    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;
-	}
-      }
-    }
-
-    // Make the booleans and encodings array correspond to the state of the GUI
-
-    setEncodings();
-    setColorFormat();
-    setOtherOptions();
-  }
-
-
-  //
-  // 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;
-
-    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;
-    } else if (choices[encodingIndex].getSelectedItem().equals("Auto")) {
-      preferredEncoding = -1;
-    }
-
-    // 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;
-    }
-
-    // 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;
-
-    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();
-
-    }
-  }
-
-  //
-  // 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) {}
-}
--- a/src/ProxyVncCanvas.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1877 +0,0 @@
-import java.awt.*;
-import java.awt.event.*;
-import java.awt.image.*;
-import java.io.*;
-import java.lang.*;
-import java.nio.ByteBuffer;
-import java.util.zip.*;
-
-import java.net.Socket;
-
-//
-//VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
-//
-
-class ProxyVncCanvas extends Canvas implements KeyListener, MouseListener,
-		MouseMotionListener {
-
-	VncProxyService viewer;
-	MyRfbProto rfb;
-	ColorModel cm8, cm24;
-	Color[] colors;
-	int bytesPixel;
-
-	int maxWidth = 0, maxHeight = 0;
-	int scalingFactor;
-	int scaledWidth, scaledHeight;
-
-	Image memImage;
-	Graphics memGraphics;
-
-	Image rawPixelsImage;
-	MemoryImageSource pixelsSource;
-	byte[] pixels8;
-	int[] pixels24;
-
-	// Update statistics.
-	long statStartTime; // time on first framebufferUpdateRequest
-	int statNumUpdates; // counter for FramebufferUpdate messages
-	int statNumTotalRects; // rectangles in FramebufferUpdate messages
-	int statNumPixelRects; // the same, but excluding pseudo-rectangles
-	int statNumRectsTight; // Tight-encoded rectangles (including JPEG)
-	int statNumRectsTightJPEG; // JPEG-compressed Tight-encoded rectangles
-	int statNumRectsZRLE; // ZRLE-encoded rectangles
-	int statNumRectsHextile; // Hextile-encoded rectangles
-	int statNumRectsRaw; // Raw-encoded rectangles
-	int statNumRectsCopy; // CopyRect rectangles
-	int statNumBytesEncoded; // number of bytes in updates, as received
-	int statNumBytesDecoded; // number of bytes, as if Raw encoding was used
-
-	// ZRLE encoder's data.
-	byte[] zrleBuf;
-	int zrleBufLen = 0;
-	byte[] zrleTilePixels8;
-	int[] zrleTilePixels24;
-	ZlibInStream zrleInStream;
-	boolean zrleRecWarningShown = false;
-
-	// Zlib encoder's data.
-	byte[] zlibBuf;
-	int zlibBufLen = 0;
-	Inflater zlibInflater;
-
-	// Tight encoder's data.
-	final static int tightZlibBufferSize = 512;
-	Inflater[] tightInflaters;
-
-	// Since JPEG images are loaded asynchronously, we have to remember
-	// their position in the framebuffer. Also, this jpegRect object is
-	// used for synchronization between the rfbThread and a JVM's thread
-	// which decodes and loads JPEG images.
-	Rectangle jpegRect;
-
-	// True if we process keyboard and mouse events.
-	boolean inputEnabled;
-
-	
-	//
-	// The constructors.
-	//
-
-	public ProxyVncCanvas(VncProxyService v, int maxWidth_, int maxHeight_)
-			throws IOException {
-
-		viewer = v;
-		maxWidth = maxWidth_;
-		maxHeight = maxHeight_;
-
-		rfb = viewer.rfb;
-
-		tightInflaters = new Inflater[4];
-
-		cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6));
-		cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
-
-		colors = new Color[256];
-		for (int i = 0; i < 256; i++)
-			colors[i] = new Color(cm8.getRGB(i));
-
-		// setPixelFormat();
-
-		inputEnabled = false;
-		// Keyboard listener is enabled even in view-only mode, to catch
-		// 'r' or 'R' key presses used to request screen update.
-		addKeyListener(this);
-	}
-
-	public ProxyVncCanvas(VncProxyService 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);
-	}
-
-	//
-	// Override the ImageObserver interface method to handle drawing of
-	// JPEG-encoded data.
-	//
-
-	public boolean imageUpdate(Image img, int infoflags, int x, int y,
-			int width, int height) {
-		if ((infoflags & (ALLBITS | ABORT)) == 0) {
-			return true; // We need more image data.
-		} else {
-			// If the whole image is available, draw it now.
-			if ((infoflags & ALLBITS) != 0) {
-				if (jpegRect != null) {
-					synchronized (jpegRect) {
-						memGraphics
-								.drawImage(img, jpegRect.x, jpegRect.y, null);
-						scheduleRepaint(jpegRect.x, jpegRect.y, jpegRect.width,
-								jpegRect.height);
-						jpegRect.notify();
-					}
-				}
-			}
-			return false; // All image data was processed.
-		}
-	}
-
-	//
-	// 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;
-			addMouseListener(this);
-			addMouseMotionListener(this);
-			if (viewer.showControls) {
-				viewer.buttonPanel.enableRemoteAccessControls(true);
-			}
-			createSoftCursor(); // scaled cursor
-		} else if (!enable && inputEnabled) {
-			inputEnabled = false;
-			removeMouseListener(this);
-			removeMouseMotionListener(this);
-			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 updateFramebufferSize() {
-
-		// Useful shortcuts.
-		int fbWidth = rfb.framebufferWidth;
-		int fbHeight = rfb.framebufferHeight;
-
-		// Calculate scaling factor for auto scaling.
-		if (maxWidth > 0 && maxHeight > 0) {
-			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();
-			}
-		}
-
-		// Images with raw pixels should be re-allocated on every change
-		// of geometry or pixel format.
-		if (bytesPixel == 1) {
-
-			pixels24 = null;
-			pixels8 = new byte[fbWidth * fbHeight];
-
-			pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm8,
-					pixels8, 0, fbWidth);
-
-			zrleTilePixels24 = null;
-			zrleTilePixels8 = new byte[64 * 64];
-
-		} else {
-
-			pixels8 = null;
-			pixels24 = new int[fbWidth * fbHeight];
-
-			pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm24,
-					pixels24, 0, fbWidth);
-
-			zrleTilePixels8 = null;
-			zrleTilePixels24 = new int[64 * 64];
-
-		}
-		pixelsSource.setAnimated(true);
-		rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource);
-
-	}
-
-	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
-		//
-
-		long count = 0;
-
-		// single thread
-/*
-		try {
-			// rfb.setSoTimeout(1000);
-			Socket newCli = rfb.accept();
-			rfb.sendInitData(newCli);
-			rfb.addSock(newCli);
-		} catch (IOException e) {
-		}
-*/
-
-		
-		 Thread accept = new Thread(new acceptThread(rfb)); 
-		 accept.start();
-		
-
-		while (true) {
-
-//			System.out.println("\ncount=" + count);
-			count++;
-			
-			rfb.regiFramebufferUpdate();
-			rfb.checkAndMark();
-//			rfb.printFramebufferUpdate();
-
-			int bufSize = (int)rfb.getNumBytesRead();
-			
-			// 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:
-						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;
-				}
-*/
-				// Request framebuffer update if needed.
-				int w = rfb.framebufferWidth;
-				int h = rfb.framebufferHeight;
-				rfb.writeFramebufferUpdateRequest(0, 0, w, h, !fullUpdateNeeded);
-
-				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);
-			}
-			bufSize = (int)rfb.getNumBytesRead() - bufSize;
-//			System.out.println("bufSize="+bufSize);
-			rfb.bufResetSend(bufSize);
-		}
-	}
-
-	//
-	// 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 {
-		handleRawRect(x, y, w, h, true);
-	}
-
-	void handleRawRect(int x, int y, int w, int h, boolean paint)
-			throws IOException {
-
-		if (bytesPixel == 1) {
-			for (int dy = y; dy < y + h; dy++) {
-				rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w);
-				if (rfb.rec != null) {
-					rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w);
-				}
-			}
-		} else {
-			byte[] buf = new byte[w * 4];
-			int i, offset;
-			for (int dy = y; dy < y + h; dy++) {
-				rfb.readFully(buf);
-				if (rfb.rec != null) {
-					rfb.rec.write(buf);
-				}
-				/*
-				 * offset = dy * rfb.framebufferWidth + x; for (i = 0; i < w;
-				 * i++) { pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16 |
-				 * (buf[i * 4 + 1] & 0xFF) << 8 | (buf[i * 4] & 0xFF); }
-				 */
-			}
-		}
-		/*
-		 * handleUpdatedPixels(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 {
-
-		rfb.readCopyRect();
-		memGraphics.copyArea(rfb.copyRectSrcX, rfb.copyRectSrcY, w, h, x
-				- rfb.copyRectSrcX, y - rfb.copyRectSrcY);
-
-		scheduleRepaint(x, y, w, h);
-	}
-
-	//
-	// Handle an RRE-encoded rectangle.
-	//
-
-	void handleRRERect(int x, int y, int w, int h) throws IOException {
-
-		int nSubrects = rfb.readU32();
-
-		byte[] bg_buf = new byte[bytesPixel];
-		rfb.readFully(bg_buf);
-		Color pixel;
-		if (bytesPixel == 1) {
-			pixel = colors[bg_buf[0] & 0xFF];
-		} else {
-			pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF,
-					bg_buf[0] & 0xFF);
-		}
-		memGraphics.setColor(pixel);
-		memGraphics.fillRect(x, y, w, h);
-
-		byte[] buf = new byte[nSubrects * (bytesPixel + 8)];
-		rfb.readFully(buf);
-		DataInputStream ds = new DataInputStream(new ByteArrayInputStream(buf));
-
-		if (rfb.rec != null) {
-			rfb.rec.writeIntBE(nSubrects);
-			rfb.rec.write(bg_buf);
-			rfb.rec.write(buf);
-		}
-
-		int sx, sy, sw, sh;
-
-		for (int j = 0; j < nSubrects; j++) {
-			if (bytesPixel == 1) {
-				pixel = colors[ds.readUnsignedByte()];
-			} else {
-				ds.skip(4);
-				pixel = new Color(buf[j * 12 + 2] & 0xFF,
-						buf[j * 12 + 1] & 0xFF, buf[j * 12] & 0xFF);
-			}
-			sx = x + ds.readUnsignedShort();
-			sy = y + ds.readUnsignedShort();
-			sw = ds.readUnsignedShort();
-			sh = ds.readUnsignedShort();
-
-			memGraphics.setColor(pixel);
-			memGraphics.fillRect(sx, sy, sw, sh);
-		}
-
-		scheduleRepaint(x, y, w, h);
-	}
-
-	//
-	// Handle a CoRRE-encoded rectangle.
-	//
-
-	void handleCoRRERect(int x, int y, int w, int h) throws IOException {
-		int nSubrects = rfb.readU32();
-
-		byte[] bg_buf = new byte[bytesPixel];
-		rfb.readFully(bg_buf);
-		Color pixel;
-		if (bytesPixel == 1) {
-			pixel = colors[bg_buf[0] & 0xFF];
-		} else {
-			pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF,
-					bg_buf[0] & 0xFF);
-		}
-		memGraphics.setColor(pixel);
-		memGraphics.fillRect(x, y, w, h);
-
-		byte[] buf = new byte[nSubrects * (bytesPixel + 4)];
-		rfb.readFully(buf);
-
-		if (rfb.rec != null) {
-			rfb.rec.writeIntBE(nSubrects);
-			rfb.rec.write(bg_buf);
-			rfb.rec.write(buf);
-		}
-
-		int sx, sy, sw, sh;
-		int i = 0;
-
-		for (int j = 0; j < nSubrects; j++) {
-			if (bytesPixel == 1) {
-				pixel = colors[buf[i++] & 0xFF];
-			} else {
-				pixel = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF,
-						buf[i] & 0xFF);
-				i += 4;
-			}
-			sx = x + (buf[i++] & 0xFF);
-			sy = y + (buf[i++] & 0xFF);
-			sw = buf[i++] & 0xFF;
-			sh = buf[i++] & 0xFF;
-
-			memGraphics.setColor(pixel);
-			memGraphics.fillRect(sx, sy, sw, sh);
-		}
-
-		scheduleRepaint(x, y, w, h);
-	}
-
-	//
-	// Handle a Hextile-encoded rectangle.
-	//
-
-	// These colors should be kept between handleHextileSubrect() calls.
-	private Color hextile_bg, hextile_fg;
-
-	void handleHextileRect(int x, int y, int w, int h) throws IOException {
-
-		hextile_bg = new Color(0);
-		hextile_fg = new Color(0);
-
-		for (int ty = y; ty < y + h; ty += 16) {
-			int th = 16;
-			if (y + h - ty < 16)
-				th = y + h - ty;
-
-			for (int tx = x; tx < x + w; tx += 16) {
-				int tw = 16;
-				if (x + w - tx < 16)
-					tw = x + w - tx;
-
-				handleHextileSubrect(tx, ty, tw, th);
-			}
-
-			// Finished with a row of tiles, now let's show it.
-			scheduleRepaint(x, y, w, h);
-		}
-	}
-
-	//
-	// Handle one tile in the Hextile-encoded data.
-	//
-
-	void handleHextileSubrect(int tx, int ty, int tw, int th)
-			throws IOException {
-
-		int subencoding = rfb.readU8();
-		if (rfb.rec != null) {
-			rfb.rec.writeByte(subencoding);
-		}
-
-		// Is it a raw-encoded sub-rectangle?
-		if ((subencoding & rfb.HextileRaw) != 0) {
-			handleRawRect(tx, ty, tw, th, false);
-			return;
-		}
-
-		// Read and draw the background if specified.
-		byte[] cbuf = new byte[bytesPixel];
-		if ((subencoding & rfb.HextileBackgroundSpecified) != 0) {
-			rfb.readFully(cbuf);
-			if (bytesPixel == 1) {
-				hextile_bg = colors[cbuf[0] & 0xFF];
-			} else {
-				hextile_bg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF,
-						cbuf[0] & 0xFF);
-			}
-			if (rfb.rec != null) {
-				rfb.rec.write(cbuf);
-			}
-		}
-		memGraphics.setColor(hextile_bg);
-		memGraphics.fillRect(tx, ty, tw, th);
-
-		// Read the foreground color if specified.
-		if ((subencoding & rfb.HextileForegroundSpecified) != 0) {
-			rfb.readFully(cbuf);
-			if (bytesPixel == 1) {
-				hextile_fg = colors[cbuf[0] & 0xFF];
-			} else {
-				hextile_fg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF,
-						cbuf[0] & 0xFF);
-			}
-			if (rfb.rec != null) {
-				rfb.rec.write(cbuf);
-			}
-		}
-
-		// Done with this tile if there is no sub-rectangles.
-		if ((subencoding & rfb.HextileAnySubrects) == 0)
-			return;
-
-		int nSubrects = rfb.readU8();
-		int bufsize = nSubrects * 2;
-		if ((subencoding & rfb.HextileSubrectsColoured) != 0) {
-			bufsize += nSubrects * bytesPixel;
-		}
-		byte[] buf = new byte[bufsize];
-		rfb.readFully(buf);
-		if (rfb.rec != null) {
-			rfb.rec.writeByte(nSubrects);
-			rfb.rec.write(buf);
-		}
-
-		int b1, b2, sx, sy, sw, sh;
-		int i = 0;
-
-		if ((subencoding & rfb.HextileSubrectsColoured) == 0) {
-
-			// Sub-rectangles are all of the same color.
-			memGraphics.setColor(hextile_fg);
-			for (int j = 0; j < nSubrects; j++) {
-				b1 = buf[i++] & 0xFF;
-				b2 = buf[i++] & 0xFF;
-				sx = tx + (b1 >> 4);
-				sy = ty + (b1 & 0xf);
-				sw = (b2 >> 4) + 1;
-				sh = (b2 & 0xf) + 1;
-				memGraphics.fillRect(sx, sy, sw, sh);
-			}
-		} else if (bytesPixel == 1) {
-
-			// BGR233 (8-bit color) version for colored sub-rectangles.
-			for (int j = 0; j < nSubrects; j++) {
-				hextile_fg = colors[buf[i++] & 0xFF];
-				b1 = buf[i++] & 0xFF;
-				b2 = buf[i++] & 0xFF;
-				sx = tx + (b1 >> 4);
-				sy = ty + (b1 & 0xf);
-				sw = (b2 >> 4) + 1;
-				sh = (b2 & 0xf) + 1;
-				memGraphics.setColor(hextile_fg);
-				memGraphics.fillRect(sx, sy, sw, sh);
-			}
-
-		} else {
-
-			// Full-color (24-bit) version for colored sub-rectangles.
-			for (int j = 0; j < nSubrects; j++) {
-				hextile_fg = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF,
-						buf[i] & 0xFF);
-				i += 4;
-				b1 = buf[i++] & 0xFF;
-				b2 = buf[i++] & 0xFF;
-				sx = tx + (b1 >> 4);
-				sy = ty + (b1 & 0xf);
-				sw = (b2 >> 4) + 1;
-				sh = (b2 & 0xf) + 1;
-				memGraphics.setColor(hextile_fg);
-				memGraphics.fillRect(sx, sy, sw, sh);
-			}
-
-		}
-	}
-
-	//
-	// 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 {
-
-		if (zrleInStream == null)
-			zrleInStream = new ZlibInStream();
-
-		int nBytes = rfb.readU32();
-		if (nBytes > 64 * 1024 * 1024)
-			throw new Exception("ZRLE decoder: illegal compressed data size");
-
-		if (zrleBuf == null || zrleBufLen < nBytes) {
-			zrleBufLen = nBytes + 4096;
-			zrleBuf = new byte[zrleBufLen];
-		}
-
-		// FIXME: Do not wait for all the data before decompression.
-		rfb.readFully(zrleBuf, 0, nBytes);
-
-		if (rfb.rec != null) {
-			if (rfb.recordFromBeginning) {
-				rfb.rec.writeIntBE(nBytes);
-				rfb.rec.write(zrleBuf, 0, nBytes);
-			} else if (!zrleRecWarningShown) {
-				System.out.println("Warning: ZRLE session can be recorded"
-						+ " only from the beginning");
-				System.out.println("Warning: Recorded file may be corrupted");
-				zrleRecWarningShown = true;
-			}
-
-		}
-
-		zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes);
-
-		for (int ty = y; ty < y + h; ty += 64) {
-
-			int th = Math.min(y + h - ty, 64);
-
-			for (int tx = x; tx < x + w; tx += 64) {
-
-				int tw = Math.min(x + w - tx, 64);
-
-				int mode = zrleInStream.readU8();
-				boolean rle = (mode & 128) != 0;
-				int palSize = mode & 127;
-				int[] palette = new int[128];
-
-				readZrlePalette(palette, palSize);
-
-				if (palSize == 1) {
-					int pix = palette[0];
-					Color c = (bytesPixel == 1) ? colors[pix] : new Color(
-							0xFF000000 | pix);
-					memGraphics.setColor(c);
-					memGraphics.fillRect(tx, ty, tw, th);
-					continue;
-				}
-
-				if (!rle) {
-					if (palSize == 0) {
-						readZrleRawPixels(tw, th);
-					} else {
-						readZrlePackedPixels(tw, th, palette, palSize);
-					}
-				} else {
-					if (palSize == 0) {
-						readZrlePlainRLEPixels(tw, th);
-					} else {
-						readZrlePackedRLEPixels(tw, th, palette);
-					}
-				}
-				handleUpdatedZrleTile(tx, ty, tw, th);
-			}
-		}
-
-		zrleInStream.reset();
-
-		scheduleRepaint(x, y, w, h);
-	}
-
-	int readPixel(InStream is) throws Exception {
-		int pix;
-
-		if (bytesPixel == 1) {
-
-			pix = is.readU8();
-		} else {
-			int p1 = is.readU8();
-			int p2 = is.readU8();
-			int p3 = is.readU8();
-			pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF);
-		}
-		return pix;
-	}
-
-	void readPixels(InStream is, int[] dst, int count) throws Exception {
-		int pix;
-		if (bytesPixel == 1) {
-			byte[] buf = new byte[count];
-			is.readBytes(buf, 0, count);
-			for (int i = 0; i < count; i++) {
-				dst[i] = (int) buf[i] & 0xFF;
-			}
-		} else {
-			byte[] buf = new byte[count * 3];
-			is.readBytes(buf, 0, count * 3);
-			for (int i = 0; i < count; i++) {
-				dst[i] = ((buf[i * 3 + 2] & 0xFF) << 16
-						| (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3] & 0xFF));
-			}
-		}
-	}
-
-	void readZrlePalette(int[] palette, int palSize) throws Exception {
-		readPixels(zrleInStream, palette, palSize);
-	}
-
-	void readZrleRawPixels(int tw, int th) throws Exception {
-		if (bytesPixel == 1) {
-			zrleInStream.readBytes(zrleTilePixels8, 0, tw * th);
-		} else {
-			readPixels(zrleInStream, zrleTilePixels24, tw * th); // /
-		}
-	}
-
-	void readZrlePackedPixels(int tw, int th, int[] palette, int palSize)
-			throws Exception {
-
-		int bppp = ((palSize > 16) ? 8 : ((palSize > 4) ? 4
-				: ((palSize > 2) ? 2 : 1)));
-		int ptr = 0;
-
-		for (int i = 0; i < th; i++) {
-			int eol = ptr + tw;
-			int b = 0;
-			int nbits = 0;
-
-			while (ptr < eol) {
-				if (nbits == 0) {
-					b = zrleInStream.readU8();
-					nbits = 8;
-				}
-				nbits -= bppp;
-				int index = (b >> nbits) & ((1 << bppp) - 1) & 127;
-				if (bytesPixel == 1) {
-					zrleTilePixels8[ptr++] = (byte) palette[index];
-				} else {
-					zrleTilePixels24[ptr++] = palette[index];
-				}
-			}
-		}
-	}
-
-	void readZrlePlainRLEPixels(int tw, int th) throws Exception {
-		int ptr = 0;
-		int end = ptr + tw * th;
-		while (ptr < end) {
-			int pix = readPixel(zrleInStream);
-			int len = 1;
-			int b;
-			do {
-				b = zrleInStream.readU8();
-				len += b;
-			} while (b == 255);
-
-			if (!(len <= end - ptr))
-				throw new Exception("ZRLE decoder: assertion failed"
-						+ " (len <= end-ptr)");
-
-			if (bytesPixel == 1) {
-				while (len-- > 0)
-					zrleTilePixels8[ptr++] = (byte) pix;
-			} else {
-				while (len-- > 0)
-					zrleTilePixels24[ptr++] = pix;
-			}
-		}
-	}
-
-	void readZrlePackedRLEPixels(int tw, int th, int[] palette)
-			throws Exception {
-
-		int ptr = 0;
-		int end = ptr + tw * th;
-		while (ptr < end) {
-			int index = zrleInStream.readU8();
-			int len = 1;
-			if ((index & 128) != 0) {
-				int b;
-				do {
-					b = zrleInStream.readU8();
-					len += b;
-				} while (b == 255);
-
-				if (!(len <= end - ptr))
-					throw new Exception("ZRLE decoder: assertion failed"
-							+ " (len <= end - ptr)");
-			}
-
-			index &= 127;
-			int pix = palette[index];
-
-			if (bytesPixel == 1) {
-				while (len-- > 0)
-					zrleTilePixels8[ptr++] = (byte) pix;
-			} else {
-				while (len-- > 0)
-					zrleTilePixels24[ptr++] = pix;
-			}
-		}
-	}
-
-	//
-	// Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update.
-	//
-
-	void handleUpdatedZrleTile(int x, int y, int w, int h) {
-		Object src, dst;
-		if (bytesPixel == 1) {
-			src = zrleTilePixels8;
-			dst = pixels8;
-		} else {
-			src = zrleTilePixels24;
-			dst = pixels24;
-		}
-		int offsetSrc = 0;
-		int offsetDst = (y * rfb.framebufferWidth + x);
-		for (int j = 0; j < h; j++) {
-			System.arraycopy(src, offsetSrc, dst, offsetDst, w);
-			offsetSrc += w;
-			offsetDst += rfb.framebufferWidth;
-		}
-		handleUpdatedPixels(x, y, w, h);
-	}
-
-	//
-	// Handle a Zlib-encoded rectangle.
-	//
-
-	void handleZlibRect(int x, int y, int w, int h) throws Exception {
-
-		int nBytes = rfb.readU32();
-
-		if (zlibBuf == null || zlibBufLen < nBytes) {
-			zlibBufLen = nBytes * 2;
-			zlibBuf = new byte[zlibBufLen];
-		}
-
-		rfb.readFully(zlibBuf, 0, nBytes);
-
-		if (rfb.rec != null && rfb.recordFromBeginning) {
-			rfb.rec.writeIntBE(nBytes);
-			rfb.rec.write(zlibBuf, 0, nBytes);
-		}
-
-		if (zlibInflater == null) {
-			zlibInflater = new Inflater();
-		}
-		zlibInflater.setInput(zlibBuf, 0, nBytes);
-
-		if (bytesPixel == 1) {
-			for (int dy = y; dy < y + h; dy++) {
-				zlibInflater.inflate(pixels8, dy * rfb.framebufferWidth + x, w);
-				if (rfb.rec != null && !rfb.recordFromBeginning)
-					rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w);
-			}
-		} else {
-			byte[] buf = new byte[w * 4];
-			int i, offset;
-			for (int dy = y; dy < y + h; dy++) {
-				zlibInflater.inflate(buf);
-				offset = dy * rfb.framebufferWidth + x;
-				for (i = 0; i < w; i++) {
-					pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16
-							| (buf[i * 4 + 1] & 0xFF) << 8
-							| (buf[i * 4] & 0xFF);
-				}
-				if (rfb.rec != null && !rfb.recordFromBeginning)
-					rfb.rec.write(buf);
-			}
-		}
-
-		handleUpdatedPixels(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 {
-
-		int comp_ctl = rfb.readU8();
-		if (rfb.rec != null) {
-			if (rfb.recordFromBeginning || comp_ctl == (rfb.TightFill << 4)
-					|| comp_ctl == (rfb.TightJpeg << 4)) {
-				// Send data exactly as received.
-				rfb.rec.writeByte(comp_ctl);
-			} else {
-				// Tell the decoder to flush each of the four zlib streams.
-				rfb.rec.writeByte(comp_ctl | 0x0F);
-			}
-		}
-
-		// Flush zlib streams if we are told by the server to do so.
-		for (int stream_id = 0; stream_id < 4; stream_id++) {
-			if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) {
-				tightInflaters[stream_id] = null;
-			}
-			comp_ctl >>= 1;
-		}
-
-		// Check correctness of subencoding value.
-		if (comp_ctl > rfb.TightMaxSubencoding) {
-			throw new Exception("Incorrect tight subencoding: " + comp_ctl);
-		}
-
-		// Handle solid-color rectangles.
-		if (comp_ctl == rfb.TightFill) {
-
-			if (bytesPixel == 1) {
-				int idx = rfb.readU8();
-				memGraphics.setColor(colors[idx]);
-				if (rfb.rec != null) {
-					rfb.rec.writeByte(idx);
-				}
-			} else {
-				byte[] buf = new byte[3];
-				rfb.readFully(buf);
-				if (rfb.rec != null) {
-					rfb.rec.write(buf);
-				}
-				Color bg = new Color(0xFF000000 | (buf[0] & 0xFF) << 16
-						| (buf[1] & 0xFF) << 8 | (buf[2] & 0xFF));
-				memGraphics.setColor(bg);
-			}
-			memGraphics.fillRect(x, y, w, h);
-			scheduleRepaint(x, y, w, h);
-			return;
-
-		}
-
-		if (comp_ctl == rfb.TightJpeg) {
-
-			statNumRectsTightJPEG++;
-
-			// Read JPEG data.
-			byte[] jpegData = new byte[rfb.readCompactLen()];
-			rfb.readFully(jpegData);
-			if (rfb.rec != null) {
-				if (!rfb.recordFromBeginning) {
-					rfb.recordCompactLen(jpegData.length);
-				}
-				rfb.rec.write(jpegData);
-			}
-
-			// Create an Image object from the JPEG data.
-			Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData);
-
-			// Remember the rectangle where the image should be drawn.
-			jpegRect = new Rectangle(x, y, w, h);
-
-			// Let the imageUpdate() method do the actual drawing, here just
-			// wait until the image is fully loaded and drawn.
-			synchronized (jpegRect) {
-				Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1,
-						this);
-				try {
-					// Wait no longer than three seconds.
-					jpegRect.wait(3000);
-				} catch (InterruptedException e) {
-					throw new Exception("Interrupted while decoding JPEG image");
-				}
-			}
-
-			// Done, jpegRect is not needed any more.
-			jpegRect = null;
-			return;
-
-		}
-
-		// Read filter id and parameters.
-		int numColors = 0, rowSize = w;
-		byte[] palette8 = new byte[2];
-		int[] palette24 = new int[256];
-		boolean useGradient = false;
-		if ((comp_ctl & rfb.TightExplicitFilter) != 0) {
-			int filter_id = rfb.readU8();
-			if (rfb.rec != null) {
-				rfb.rec.writeByte(filter_id);
-			}
-			if (filter_id == rfb.TightFilterPalette) {
-				numColors = rfb.readU8() + 1;
-				if (rfb.rec != null) {
-					rfb.rec.writeByte(numColors - 1);
-				}
-				if (bytesPixel == 1) {
-					if (numColors != 2) {
-						throw new Exception("Incorrect tight palette size: "
-								+ numColors);
-					}
-					rfb.readFully(palette8);
-					if (rfb.rec != null) {
-						rfb.rec.write(palette8);
-					}
-				} else {
-					byte[] buf = new byte[numColors * 3];
-					rfb.readFully(buf);
-					if (rfb.rec != null) {
-						rfb.rec.write(buf);
-					}
-					for (int i = 0; i < numColors; i++) {
-						palette24[i] = ((buf[i * 3] & 0xFF) << 16
-								| (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3 + 2] & 0xFF));
-					}
-				}
-				if (numColors == 2)
-					rowSize = (w + 7) / 8;
-			} else if (filter_id == rfb.TightFilterGradient) {
-				useGradient = true;
-			} else if (filter_id != rfb.TightFilterCopy) {
-				throw new Exception("Incorrect tight filter id: " + filter_id);
-			}
-		}
-		if (numColors == 0 && bytesPixel == 4)
-			rowSize *= 3;
-
-		// Read, optionally uncompress and decode data.
-		int dataSize = h * rowSize;
-		if (dataSize < rfb.TightMinToCompress) {
-			// Data size is small - not compressed with zlib.
-			if (numColors != 0) {
-				// Indexed colors.
-				byte[] indexedData = new byte[dataSize];
-				rfb.readFully(indexedData);
-				if (rfb.rec != null) {
-					rfb.rec.write(indexedData);
-				}
-				if (numColors == 2) {
-					// Two colors.
-					if (bytesPixel == 1) {
-						decodeMonoData(x, y, w, h, indexedData, palette8);
-					} else {
-						decodeMonoData(x, y, w, h, indexedData, palette24);
-					}
-				} else {
-					// 3..255 colors (assuming bytesPixel == 4).
-					int i = 0;
-					for (int dy = y; dy < y + h; dy++) {
-						for (int dx = x; dx < x + w; dx++) {
-							pixels24[dy * rfb.framebufferWidth + dx] = palette24[indexedData[i++] & 0xFF];
-						}
-					}
-				}
-			} else if (useGradient) {
-				// "Gradient"-processed data
-				byte[] buf = new byte[w * h * 3];
-				rfb.readFully(buf);
-				if (rfb.rec != null) {
-					rfb.rec.write(buf);
-				}
-				decodeGradientData(x, y, w, h, buf);
-			} else {
-				// Raw truecolor data.
-				if (bytesPixel == 1) {
-					for (int dy = y; dy < y + h; dy++) {
-						rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w);
-						if (rfb.rec != null) {
-							rfb.rec.write(pixels8, dy * rfb.framebufferWidth
-									+ x, w);
-						}
-					}
-				} else {
-					byte[] buf = new byte[w * 3];
-					int i, offset;
-					for (int dy = y; dy < y + h; dy++) {
-						rfb.readFully(buf);
-						if (rfb.rec != null) {
-							rfb.rec.write(buf);
-						}
-						offset = dy * rfb.framebufferWidth + x;
-						for (i = 0; i < w; i++) {
-							pixels24[offset + i] = (buf[i * 3] & 0xFF) << 16
-									| (buf[i * 3 + 1] & 0xFF) << 8
-									| (buf[i * 3 + 2] & 0xFF);
-						}
-					}
-				}
-			}
-		} else {
-			// Data was compressed with zlib.
-			int zlibDataLen = rfb.readCompactLen();
-			byte[] zlibData = new byte[zlibDataLen];
-			rfb.readFully(zlibData);
-			if (rfb.rec != null && rfb.recordFromBeginning) {
-				rfb.rec.write(zlibData);
-			}
-			int stream_id = comp_ctl & 0x03;
-			if (tightInflaters[stream_id] == null) {
-				tightInflaters[stream_id] = new Inflater();
-			}
-			Inflater myInflater = tightInflaters[stream_id];
-			myInflater.setInput(zlibData);
-			byte[] buf = new byte[dataSize];
-			myInflater.inflate(buf);
-			if (rfb.rec != null && !rfb.recordFromBeginning) {
-				rfb.recordCompressedData(buf);
-			}
-
-			if (numColors != 0) {
-				// Indexed colors.
-				if (numColors == 2) {
-					// Two colors.
-					if (bytesPixel == 1) {
-						decodeMonoData(x, y, w, h, buf, palette8);
-					} else {
-						decodeMonoData(x, y, w, h, buf, palette24);
-					}
-				} else {
-					// More than two colors (assuming bytesPixel == 4).
-					int i = 0;
-					for (int dy = y; dy < y + h; dy++) {
-						for (int dx = x; dx < x + w; dx++) {
-							pixels24[dy * rfb.framebufferWidth + dx] = palette24[buf[i++] & 0xFF];
-						}
-					}
-				}
-			} else if (useGradient) {
-				// Compressed "Gradient"-filtered data (assuming bytesPixel ==
-				// 4).
-				decodeGradientData(x, y, w, h, buf);
-			} else {
-				// Compressed truecolor data.
-				if (bytesPixel == 1) {
-					int destOffset = y * rfb.framebufferWidth + x;
-					for (int dy = 0; dy < h; dy++) {
-						System.arraycopy(buf, dy * w, pixels8, destOffset, w);
-						destOffset += rfb.framebufferWidth;
-					}
-				} else {
-					int srcOffset = 0;
-					int destOffset, i;
-					for (int dy = 0; dy < h; dy++) {
-						myInflater.inflate(buf);
-						destOffset = (y + dy) * rfb.framebufferWidth + x;
-						for (i = 0; i < w; i++) {
-							pixels24[destOffset + i] = (buf[srcOffset] & 0xFF) << 16
-									| (buf[srcOffset + 1] & 0xFF) << 8
-									| (buf[srcOffset + 2] & 0xFF);
-							srcOffset += 3;
-						}
-					}
-				}
-			}
-		}
-
-		handleUpdatedPixels(x, y, w, h);
-		scheduleRepaint(x, y, w, h);
-	}
-
-	//
-	// Decode 1bpp-encoded bi-color rectangle (8-bit and 24-bit versions).
-	//
-
-	void decodeMonoData(int x, int y, int w, int h, byte[] src, byte[] palette) {
-
-		int dx, dy, n;
-		int i = y * rfb.framebufferWidth + x;
-		int rowBytes = (w + 7) / 8;
-		byte b;
-
-		for (dy = 0; dy < h; dy++) {
-			for (dx = 0; dx < w / 8; dx++) {
-				b = src[dy * rowBytes + dx];
-				for (n = 7; n >= 0; n--)
-					pixels8[i++] = palette[b >> n & 1];
-			}
-			for (n = 7; n >= 8 - w % 8; n--) {
-				pixels8[i++] = palette[src[dy * rowBytes + dx] >> n & 1];
-			}
-			i += (rfb.framebufferWidth - w);
-		}
-	}
-
-	void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette) {
-
-		int dx, dy, n;
-		int i = y * rfb.framebufferWidth + x;
-		int rowBytes = (w + 7) / 8;
-		byte b;
-
-		for (dy = 0; dy < h; dy++) {
-			for (dx = 0; dx < w / 8; dx++) {
-				b = src[dy * rowBytes + dx];
-				for (n = 7; n >= 0; n--)
-					pixels24[i++] = palette[b >> n & 1];
-			}
-			for (n = 7; n >= 8 - w % 8; n--) {
-				pixels24[i++] = palette[src[dy * rowBytes + dx] >> n & 1];
-			}
-			i += (rfb.framebufferWidth - w);
-		}
-	}
-
-	//
-	// Decode data processed with the "Gradient" filter.
-	//
-
-	void decodeGradientData(int x, int y, int w, int h, byte[] buf) {
-
-		int dx, dy, c;
-		byte[] prevRow = new byte[w * 3];
-		byte[] thisRow = new byte[w * 3];
-		byte[] pix = new byte[3];
-		int[] est = new int[3];
-
-		int offset = y * rfb.framebufferWidth + x;
-
-		for (dy = 0; dy < h; dy++) {
-
-			/* First pixel in a row */
-			for (c = 0; c < 3; c++) {
-				pix[c] = (byte) (prevRow[c] + buf[dy * w * 3 + c]);
-				thisRow[c] = pix[c];
-			}
-			pixels24[offset++] = (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8
-					| (pix[2] & 0xFF);
-
-			/* Remaining pixels of a row */
-			for (dx = 1; dx < w; dx++) {
-				for (c = 0; c < 3; c++) {
-					est[c] = ((prevRow[dx * 3 + c] & 0xFF) + (pix[c] & 0xFF) - (prevRow[(dx - 1)
-							* 3 + c] & 0xFF));
-					if (est[c] > 0xFF) {
-						est[c] = 0xFF;
-					} else if (est[c] < 0x00) {
-						est[c] = 0x00;
-					}
-					pix[c] = (byte) (est[c] + buf[(dy * w + dx) * 3 + c]);
-					thisRow[dx * 3 + c] = pix[c];
-				}
-				pixels24[offset++] = (pix[0] & 0xFF) << 16
-						| (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
-			}
-
-			System.arraycopy(thisRow, 0, prevRow, 0, w * 3);
-			offset += (rfb.framebufferWidth - w);
-		}
-	}
-
-	//
-	// Display newly updated area of pixels.
-	//
-
-	void handleUpdatedPixels(int x, int y, int w, int h) {
-
-		// Draw updated pixels of the off-screen image.
-		pixelsSource.newPixels(x, y, w, h);
-		memGraphics.setClip(x, y, w, h);
-		memGraphics.drawImage(rawPixelsImage, 0, 0, null);
-		memGraphics.setClip(0, 0, rfb.framebufferWidth, rfb.framebufferHeight);
-	}
-
-	//
-	// Tell JVM to repaint specified desktop area.
-	//
-
-	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);
-	}
-
-	public 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();
-	}
-
-	public void processLocalMouseEvent(MouseEvent evt, boolean moved) {
-		if (viewer.rfb != null && rfb.inNormalProtocol) {
-			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();
-			}
-		}
-	}
-
-	//
-	// Ignored events.
-	//
-
-	public void mouseClicked(MouseEvent evt) {
-	}
-
-	public void mouseEntered(MouseEvent evt) {
-	}
-
-	public void mouseExited(MouseEvent evt) {
-	}
-
-	//
-	// 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;
-	}
-
-	// ////////////////////////////////////////////////////////////////
-	//
-	// Handle cursor shape updates (XCursor and RichCursor encodings).
-	//
-
-	boolean showSoftCursor = false;
-
-	MemoryImageSource softCursorSource;
-	Image softCursor;
-
-	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 * bytesPixel + 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 pixByte, 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);
-		}
-	}
-}
--- a/src/RecordingFrame.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,311 +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.
-//
-
-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("TightVNC 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);
-
-    }
-  }
-}
--- a/src/ReloginPanel.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +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
-//
-
-
-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());
-  }
-}
--- a/src/RfbProto.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1379 +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
-//
-
-import java.io.*;
-import java.awt.*;
-import java.awt.event.*;
-import java.net.Socket;
-import java.net.ServerSocket;
-import java.util.zip.*;
-import java.nio.*;
-
-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";
-
-	// Vendor signatures: standard VNC/RealVNC, TridiaVNC, and TightVNC
-	final static String StandardVendor = "STDV", TridiaVncVendor = "TRDV",
-			TightVncVendor = "TGHT";
-
-	// Security types
-	final static int SecTypeInvalid = 0, SecTypeNone = 1, SecTypeVncAuth = 2,
-			SecTypeTight = 16;
-
-	// Supported tunneling types
-	final static int NoTunneling = 0;
-	final static String SigNoTunneling = "NOTUNNEL";
-
-	// Supported authentication types
-	final static int AuthNone = 1, AuthVNC = 2, AuthUnixLogin = 129;
-	final static String SigAuthNone = "NOAUTH__", SigAuthVNC = "VNCAUTH_",
-			SigAuthUnixLogin = "ULGNAUTH";
-
-	// 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;
-
-	// Non-standard server-to-client messages
-	final static int EndOfContinuousUpdates = 150;
-	final static String SigEndOfContinuousUpdates = "CUS_EOCU";
-
-	// Standard client-to-server messages
-	final static int SetPixelFormat = 0, FixColourMapEntries = 1,
-			SetEncodings = 2, FramebufferUpdateRequest = 3, KeyboardEvent = 4,
-			PointerEvent = 5, ClientCutText = 6;
-
-	// Non-standard client-to-server messages
-	final static int EnableContinuousUpdates = 150;
-	final static String SigEnableContinuousUpdates = "CUC_ENCU";
-
-	// 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 String SigEncodingRaw = "RAW_____",
-			SigEncodingCopyRect = "COPYRECT", SigEncodingRRE = "RRE_____",
-			SigEncodingCoRRE = "CORRE___", SigEncodingHextile = "HEXTILE_",
-			SigEncodingZlib = "ZLIB____", SigEncodingTight = "TIGHT___",
-			SigEncodingZRLE = "ZRLE____",
-			SigEncodingCompressLevel0 = "COMPRLVL",
-			SigEncodingQualityLevel0 = "JPEGQLVL",
-			SigEncodingXCursor = "X11CURSR",
-			SigEncodingRichCursor = "RCHCURSR",
-			SigEncodingPointerPos = "POINTPOS",
-			SigEncodingLastRect = "LASTRECT",
-			SigEncodingNewFBSize = "NEWFBSIZ";
-
-	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;
-	protected DataInputStream is;
-//	private long numBytesRead = 0;
-	protected 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 will be set to false if the startSession() was called after
-	// we have received at least one Zlib-, ZRLE- or Tight-encoded
-	// framebuffer update.
-	boolean recordFromBeginning = true;
-
-	// 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;
-	boolean protocolTightVNC;
-	CapsContainer tunnelCaps, authCaps;
-	CapsContainer serverMsgCaps, clientMsgCaps;
-	CapsContainer encodingCaps;
-
-	// If true, informs that the RFB socket was closed.
-//	private boolean closed;
-	protected 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);
-		} 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;
-	}
-	
-	RfbProto(String h, int p) throws IOException {
-		host = h;
-		port = p;
-
-		sock = new Socket(host, port);
-		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());
-		}
-		protocolTightVNC = false;
-		initCapabilities();
-	}
-
-	//
-	// 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 out if the server supports TightVNC protocol extensions
-		for (int i = 0; i < nSecTypes; i++) {
-			if (secTypes[i] == SecTypeTight) {
-				protocolTightVNC = true;
-				os.write(SecTypeTight);
-				return SecTypeTight;
-			}
-		}
-
-		// Find first supported security type.
-		for (int i = 0; i < nSecTypes; i++) {
-			if (secTypes[i] == SecTypeNone || secTypes[i] == SecTypeVncAuth) {
-				secType = secTypes[i];
-				break;
-			}
-		}
-
-		if (secType == SecTypeInvalid) {
-			throw new Exception("Server did not offer supported security type");
-		} else {
-			os.write(secType);
-		}
-
-		return secType;
-	}
-
-	//
-	// 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");
-	}
-
-	//
-	// 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));
-	}
-
-	//
-	// Initialize capability lists (TightVNC protocol extensions).
-	//
-
-	void initCapabilities() {
-		tunnelCaps = new CapsContainer();
-		authCaps = new CapsContainer();
-		serverMsgCaps = new CapsContainer();
-		clientMsgCaps = new CapsContainer();
-		encodingCaps = new CapsContainer();
-
-		// Supported authentication methods
-		authCaps.add(AuthNone, StandardVendor, SigAuthNone, "No authentication");
-		authCaps.add(AuthVNC, StandardVendor, SigAuthVNC,
-				"Standard VNC password authentication");
-
-		// Supported non-standard server-to-client messages
-		// [NONE]
-
-		// Supported non-standard client-to-server messages
-		// [NONE]
-
-		// Supported encoding types
-		encodingCaps.add(EncodingCopyRect, StandardVendor, SigEncodingCopyRect,
-				"Standard CopyRect encoding");
-		encodingCaps.add(EncodingRRE, StandardVendor, SigEncodingRRE,
-				"Standard RRE encoding");
-		encodingCaps.add(EncodingCoRRE, StandardVendor, SigEncodingCoRRE,
-				"Standard CoRRE encoding");
-		encodingCaps.add(EncodingHextile, StandardVendor, SigEncodingHextile,
-				"Standard Hextile encoding");
-		encodingCaps.add(EncodingZRLE, StandardVendor, SigEncodingZRLE,
-				"Standard ZRLE encoding");
-		encodingCaps.add(EncodingZlib, TridiaVncVendor, SigEncodingZlib,
-				"Zlib encoding");
-		encodingCaps.add(EncodingTight, TightVncVendor, SigEncodingTight,
-				"Tight encoding");
-
-		// Supported pseudo-encoding types
-		encodingCaps.add(EncodingCompressLevel0, TightVncVendor,
-				SigEncodingCompressLevel0, "Compression level");
-		encodingCaps.add(EncodingQualityLevel0, TightVncVendor,
-				SigEncodingQualityLevel0, "JPEG quality level");
-		encodingCaps.add(EncodingXCursor, TightVncVendor, SigEncodingXCursor,
-				"X-style cursor shape update");
-		encodingCaps.add(EncodingRichCursor, TightVncVendor,
-				SigEncodingRichCursor, "Rich-color cursor shape update");
-		encodingCaps.add(EncodingPointerPos, TightVncVendor,
-				SigEncodingPointerPos, "Pointer position update");
-		encodingCaps.add(EncodingLastRect, TightVncVendor, SigEncodingLastRect,
-				"LastRect protocol extension");
-		encodingCaps.add(EncodingNewFBSize, TightVncVendor,
-				SigEncodingNewFBSize, "Framebuffer size change");
-	}
-
-	//
-	// Setup tunneling (TightVNC protocol extensions)
-	//
-
-	void setupTunneling() throws IOException {
-		int nTunnelTypes = readU32();
-		if (nTunnelTypes != 0) {
-			readCapabilityList(tunnelCaps, nTunnelTypes);
-
-			// We don't support tunneling yet.
-			writeInt(NoTunneling);
-		}
-	}
-
-	//
-	// Negotiate authentication scheme (TightVNC protocol extensions)
-	//
-
-	int negotiateAuthenticationTight() throws Exception {
-		int nAuthTypes = readU32();
-		if (nAuthTypes == 0)
-			return AuthNone;
-
-		readCapabilityList(authCaps, nAuthTypes);
-		for (int i = 0; i < authCaps.numEnabled(); i++) {
-			int authType = authCaps.getByOrder(i);
-			if (authType == AuthNone || authType == AuthVNC) {
-				writeInt(authType);
-				return authType;
-			}
-		}
-		throw new Exception("No suitable authentication scheme found");
-	}
-
-	//
-	// Read a capability list (TightVNC protocol extensions)
-	//
-
-	void readCapabilityList(CapsContainer caps, int count) throws IOException {
-		int code;
-		byte[] vendor = new byte[4];
-		byte[] name = new byte[8];
-		for (int i = 0; i < count; i++) {
-			code = readU32();
-			readFully(vendor);
-			readFully(name);
-			caps.enable(new CapabilityInfo(code, vendor, name));
-		}
-	}
-
-	//
-	// 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);
-*/
-			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);
-
-		// Read interaction capabilities (TightVNC protocol extensions)
-		if (protocolTightVNC) {
-			int nServerMessageTypes = readU16();
-			int nClientMessageTypes = readU16();
-			int nEncodingTypes = readU16();
-			readU16();
-			readCapabilityList(serverMsgCaps, nServerMessageTypes);
-			readCapabilityList(clientMsgCaps, nClientMessageTypes);
-			readCapabilityList(encodingCaps, nEncodingTypes);
-		}
-
-		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.
-		//
-		if (wereZlibUpdates)
-			recordFromBeginning = false;
-
-		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();
-		// System.out.println(updateNRects);
-
-		// If the session is being recorded:
-		if (rec != null) {
-			rec.writeByte(FramebufferUpdate);
-			rec.writeByte(0);
-			rec.writeShortBE(updateNRects);
-		}
-
-		numUpdatesInSession++;
-	}
-
-	// 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();
-		// System.out.println("readU16&32");
-
-		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 (updateRectEncoding == EncodingZlib && !recordFromBeginning) {
-				// Here we cannot write Zlib-encoded rectangles because the
-				// decoder won't be able to reproduce zlib stream state.
-				if (!zlibWarningShown) {
-					System.out.println("Warning: Raw encoding will be used "
-							+ "instead of Zlib in recorded session.");
-					zlibWarningShown = true;
-				}
-				rec.writeIntBE(EncodingRaw);
-			} else {
-				rec.writeIntBE(updateRectEncoding);
-				if (updateRectEncoding == EncodingTight && !recordFromBeginning
-						&& !tightWarningShown) {
-					System.out.println("Warning: Re-compressing Tight-encoded "
-							+ "updates for session recording.");
-					tightWarningShown = true;
-				}
-			}
-		}
-
-		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 CopyRect source X and Y.
-
-	int copyRectSrcX, copyRectSrcY;
-
-	void readCopyRect() throws IOException {
-		copyRectSrcX = readU16();
-		copyRectSrcY = readU16();
-
-		// If the session is being recorded:
-		if (rec != null) {
-			rec.writeShortBE(copyRectSrcX);
-			rec.writeShortBE(copyRectSrcY);
-		}
-	}
-
-	//
-	// 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;
-			}
-		}
-
-		if (rec != null && recordFromBeginning)
-			for (int i = 0; i < byteCount; i++)
-				rec.writeByte(portion[i]);
-
-		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;
-	}
-
-	//
-	// Compress and write the data into the recorded session file. This
-	// method assumes the recording is on (rec != null).
-	//
-
-	void recordCompressedData(byte[] data, int off, int len) throws IOException {
-		Deflater deflater = new Deflater();
-		deflater.setInput(data, off, len);
-		int bufSize = len + len / 100 + 12;
-		byte[] buf = new byte[bufSize];
-		deflater.finish();
-		int compressedSize = deflater.deflate(buf);
-		recordCompactLen(compressedSize);
-		rec.write(buf, 0, compressedSize);
-	}
-
-	void recordCompressedData(byte[] data) throws IOException {
-		recordCompressedData(data, 0, data.length);
-	}
-
-	//
-	// Write an integer in compact representation (1..3 bytes) into the
-	// recorded session file. This method assumes the recording is on
-	// (rec != null).
-	//
-
-	void recordCompactLen(int len) throws IOException {
-		byte[] buf = new byte[3];
-		int bytes = 0;
-		buf[bytes++] = (byte) (len & 0x7F);
-		if (len > 0x7F) {
-			buf[bytes - 1] |= 0x80;
-			buf[bytes++] = (byte) (len >> 7 & 0x7F);
-			if (len > 0x3FFF) {
-				buf[bytes - 1] |= 0x80;
-				buf[bytes++] = (byte) (len >> 14 & 0xFF);
-			}
-		}
-		rec.write(buf, 0, bytes);
-	}
-
-	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;
-	}
-}
--- a/src/SessionRecorder.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,193 +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.
-//
-
-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);
-  }
-
-}
-
--- a/src/SocketFactory.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +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.
-//
-
-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;
-}
--- a/src/VncCanvas.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1918 +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.
-//
-
-import java.awt.*;
-import java.awt.event.*;
-import java.awt.image.*;
-import java.io.*;
-import java.lang.*;
-import java.nio.ByteBuffer;
-import java.util.zip.*;
-
-import java.net.Socket;
-
-//
-// VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
-//
-
-class VncCanvas extends Canvas implements KeyListener, MouseListener,
-		MouseMotionListener {
-
-	VncViewer viewer;
-	MyRfbProto rfb;
-	ColorModel cm8, cm24;
-	Color[] colors;
-	int bytesPixel;
-
-	int maxWidth = 0, maxHeight = 0;
-	int scalingFactor;
-	int scaledWidth, scaledHeight;
-
-	Image memImage;
-	Graphics memGraphics;
-
-	Image rawPixelsImage;
-	MemoryImageSource pixelsSource;
-	byte[] pixels8;
-	int[] pixels24;
-
-	// Update statistics.
-	long statStartTime; // time on first framebufferUpdateRequest
-	int statNumUpdates; // counter for FramebufferUpdate messages
-	int statNumTotalRects; // rectangles in FramebufferUpdate messages
-	int statNumPixelRects; // the same, but excluding pseudo-rectangles
-	int statNumRectsTight; // Tight-encoded rectangles (including JPEG)
-	int statNumRectsTightJPEG; // JPEG-compressed Tight-encoded rectangles
-	int statNumRectsZRLE; // ZRLE-encoded rectangles
-	int statNumRectsHextile; // Hextile-encoded rectangles
-	int statNumRectsRaw; // Raw-encoded rectangles
-	int statNumRectsCopy; // CopyRect rectangles
-	int statNumBytesEncoded; // number of bytes in updates, as received
-	int statNumBytesDecoded; // number of bytes, as if Raw encoding was used
-
-	// ZRLE encoder's data.
-	byte[] zrleBuf;
-	int zrleBufLen = 0;
-	byte[] zrleTilePixels8;
-	int[] zrleTilePixels24;
-	ZlibInStream zrleInStream;
-	boolean zrleRecWarningShown = false;
-
-	// Zlib encoder's data.
-	byte[] zlibBuf;
-	int zlibBufLen = 0;
-	Inflater zlibInflater;
-
-	// Tight encoder's data.
-	final static int tightZlibBufferSize = 512;
-	Inflater[] tightInflaters;
-
-	// Since JPEG images are loaded asynchronously, we have to remember
-	// their position in the framebuffer. Also, this jpegRect object is
-	// used for synchronization between the rfbThread and a JVM's thread
-	// which decodes and loads JPEG images.
-	Rectangle jpegRect;
-
-	// True if we process keyboard and mouse events.
-	boolean inputEnabled;
-
-	//
-	// 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;
-
-		tightInflaters = new Inflater[4];
-
-		cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6));
-		cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
-
-		colors = new Color[256];
-		for (int i = 0; i < 256; i++)
-			colors[i] = new Color(cm8.getRGB(i));
-
-		setPixelFormat();
-
-		inputEnabled = false;
-		if (!viewer.options.viewOnly)
-			enableInput(true);
-
-		// Keyboard listener is enabled even in view-only mode, to catch
-		// 'r' or 'R' key presses used to request screen update.
-		addKeyListener(this);
-	}
-
-	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);
-	}
-
-	//
-	// Override the ImageObserver interface method to handle drawing of
-	// JPEG-encoded data.
-	//
-
-	public boolean imageUpdate(Image img, int infoflags, int x, int y,
-			int width, int height) {
-		if ((infoflags & (ALLBITS | ABORT)) == 0) {
-			return true; // We need more image data.
-		} else {
-			// If the whole image is available, draw it now.
-			if ((infoflags & ALLBITS) != 0) {
-				if (jpegRect != null) {
-					synchronized (jpegRect) {
-						memGraphics
-								.drawImage(img, jpegRect.x, jpegRect.y, null);
-						scheduleRepaint(jpegRect.x, jpegRect.y, jpegRect.width,
-								jpegRect.height);
-						jpegRect.notify();
-					}
-				}
-			}
-			return false; // All image data was processed.
-		}
-	}
-
-	//
-	// 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;
-			addMouseListener(this);
-			addMouseMotionListener(this);
-			if (viewer.showControls) {
-				viewer.buttonPanel.enableRemoteAccessControls(true);
-			}
-			createSoftCursor(); // scaled cursor
-		} else if (!enable && inputEnabled) {
-			inputEnabled = false;
-			removeMouseListener(this);
-			removeMouseMotionListener(this);
-			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 updateFramebufferSize() {
-
-		// Useful shortcuts.
-		int fbWidth = rfb.framebufferWidth;
-		int fbHeight = rfb.framebufferHeight;
-
-		// Calculate scaling factor for auto scaling.
-		if (maxWidth > 0 && maxHeight > 0) {
-			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();
-			}
-		}
-
-		// Images with raw pixels should be re-allocated on every change
-		// of geometry or pixel format.
-		if (bytesPixel == 1) {
-
-			pixels24 = null;
-			pixels8 = new byte[fbWidth * fbHeight];
-
-			pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm8,
-					pixels8, 0, fbWidth);
-
-			zrleTilePixels24 = null;
-			zrleTilePixels8 = new byte[64 * 64];
-
-		} else {
-
-			pixels8 = null;
-			pixels24 = new int[fbWidth * fbHeight];
-
-			pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm24,
-					pixels24, 0, fbWidth);
-
-			zrleTilePixels8 = null;
-			zrleTilePixels24 = new int[64 * 64];
-
-		}
-		pixelsSource.setAnimated(true);
-		rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource);
-
-		// Update the size of desktop containers.
-		if (viewer.inSeparateFrame) {
-			if (viewer.desktopScrollPane != null)
-				resizeDesktopFrame();
-		} 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
-		//
-
-		long count = 0;
-
-/*
-		try {
-			// rfb.setSoTimeout(1000);
-			Socket newCli = rfb.accept();
-			rfb.sendInitData(newCli);
-			rfb.addSock(newCli);
-		} catch (IOException e) {
-		}
-*/
-		 Thread accept = new Thread(new acceptThread(rfb)); 
-		 accept.start();
-
-		while (true) {
-
-			
-			if(!rfb.ready())continue;
-			System.out.println("\ncount=" + count);
-			count++;
-			System.out.println("ready rfb.available()="+rfb.available());			
-			
-			rfb.regiFramebufferUpdate();
-			rfb.printFramebufferUpdate();
-			rfb.checkAndMark();
-			
-			System.out.println("rfb.available()="+rfb.available());
-
-//			rfb.printNumBytesRead();
-			long bufSize = rfb.getNumBytesRead();			
-			// 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:
-						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;
-				}
-
-				// Request framebuffer update if needed.
-				int w = rfb.framebufferWidth;
-				int h = rfb.framebufferHeight;
-				rfb.writeFramebufferUpdateRequest(0, 0, w, h, !fullUpdateNeeded);
-
-				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);
-			}
-			bufSize = rfb.getNumBytesRead() - bufSize;
-			System.out.println("bufSize="+bufSize);
-			rfb.bufResetSend((int)bufSize);
-			if(rfb.available() < bufSize){
-				System.out.println("rfb.available() < bufSize");
-			
-			}
-						
-		}
-	}
-
-	//
-	// 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 {
-		handleRawRect(x, y, w, h, true);
-	}
-
-	void handleRawRect(int x, int y, int w, int h, boolean paint)
-			throws IOException {
-
-		if (bytesPixel == 1) {
-			for (int dy = y; dy < y + h; dy++) {
-				rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w);
-				if (rfb.rec != null) {
-					rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w);
-				}
-			}
-		} else {
-			byte[] buf = new byte[w * 4];
-			int i, offset;
-			for (int dy = y; dy < y + h; dy++) {
-				rfb.readFully(buf);
-				if (rfb.rec != null) {
-					rfb.rec.write(buf);
-				}
-				offset = dy * rfb.framebufferWidth + x;
-				for (i = 0; i < w; i++) {
-					pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16
-							| (buf[i * 4 + 1] & 0xFF) << 8
-							| (buf[i * 4] & 0xFF);
-				}
-			}
-		}
-
-		handleUpdatedPixels(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 {
-
-		rfb.readCopyRect();
-		memGraphics.copyArea(rfb.copyRectSrcX, rfb.copyRectSrcY, w, h, x
-				- rfb.copyRectSrcX, y - rfb.copyRectSrcY);
-
-		scheduleRepaint(x, y, w, h);
-	}
-
-	//
-	// Handle an RRE-encoded rectangle.
-	//
-
-	void handleRRERect(int x, int y, int w, int h) throws IOException {
-
-		int nSubrects = rfb.readU32();
-
-		byte[] bg_buf = new byte[bytesPixel];
-		rfb.readFully(bg_buf);
-		Color pixel;
-		if (bytesPixel == 1) {
-			pixel = colors[bg_buf[0] & 0xFF];
-		} else {
-			pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF,
-					bg_buf[0] & 0xFF);
-		}
-		memGraphics.setColor(pixel);
-		memGraphics.fillRect(x, y, w, h);
-
-		byte[] buf = new byte[nSubrects * (bytesPixel + 8)];
-		rfb.readFully(buf);
-		DataInputStream ds = new DataInputStream(new ByteArrayInputStream(buf));
-
-		if (rfb.rec != null) {
-			rfb.rec.writeIntBE(nSubrects);
-			rfb.rec.write(bg_buf);
-			rfb.rec.write(buf);
-		}
-
-		int sx, sy, sw, sh;
-
-		for (int j = 0; j < nSubrects; j++) {
-			if (bytesPixel == 1) {
-				pixel = colors[ds.readUnsignedByte()];
-			} else {
-				ds.skip(4);
-				pixel = new Color(buf[j * 12 + 2] & 0xFF,
-						buf[j * 12 + 1] & 0xFF, buf[j * 12] & 0xFF);
-			}
-			sx = x + ds.readUnsignedShort();
-			sy = y + ds.readUnsignedShort();
-			sw = ds.readUnsignedShort();
-			sh = ds.readUnsignedShort();
-
-			memGraphics.setColor(pixel);
-			memGraphics.fillRect(sx, sy, sw, sh);
-		}
-
-		scheduleRepaint(x, y, w, h);
-	}
-
-	//
-	// Handle a CoRRE-encoded rectangle.
-	//
-
-	void handleCoRRERect(int x, int y, int w, int h) throws IOException {
-		int nSubrects = rfb.readU32();
-
-		byte[] bg_buf = new byte[bytesPixel];
-		rfb.readFully(bg_buf);
-		Color pixel;
-		if (bytesPixel == 1) {
-			pixel = colors[bg_buf[0] & 0xFF];
-		} else {
-			pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF,
-					bg_buf[0] & 0xFF);
-		}
-		memGraphics.setColor(pixel);
-		memGraphics.fillRect(x, y, w, h);
-
-		byte[] buf = new byte[nSubrects * (bytesPixel + 4)];
-		rfb.readFully(buf);
-
-		if (rfb.rec != null) {
-			rfb.rec.writeIntBE(nSubrects);
-			rfb.rec.write(bg_buf);
-			rfb.rec.write(buf);
-		}
-
-		int sx, sy, sw, sh;
-		int i = 0;
-
-		for (int j = 0; j < nSubrects; j++) {
-			if (bytesPixel == 1) {
-				pixel = colors[buf[i++] & 0xFF];
-			} else {
-				pixel = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF,
-						buf[i] & 0xFF);
-				i += 4;
-			}
-			sx = x + (buf[i++] & 0xFF);
-			sy = y + (buf[i++] & 0xFF);
-			sw = buf[i++] & 0xFF;
-			sh = buf[i++] & 0xFF;
-
-			memGraphics.setColor(pixel);
-			memGraphics.fillRect(sx, sy, sw, sh);
-		}
-
-		scheduleRepaint(x, y, w, h);
-	}
-
-	//
-	// Handle a Hextile-encoded rectangle.
-	//
-
-	// These colors should be kept between handleHextileSubrect() calls.
-	private Color hextile_bg, hextile_fg;
-
-	void handleHextileRect(int x, int y, int w, int h) throws IOException {
-
-		hextile_bg = new Color(0);
-		hextile_fg = new Color(0);
-
-		for (int ty = y; ty < y + h; ty += 16) {
-			int th = 16;
-			if (y + h - ty < 16)
-				th = y + h - ty;
-
-			for (int tx = x; tx < x + w; tx += 16) {
-				int tw = 16;
-				if (x + w - tx < 16)
-					tw = x + w - tx;
-
-				handleHextileSubrect(tx, ty, tw, th);
-			}
-
-			// Finished with a row of tiles, now let's show it.
-			scheduleRepaint(x, y, w, h);
-		}
-	}
-
-	//
-	// Handle one tile in the Hextile-encoded data.
-	//
-
-	void handleHextileSubrect(int tx, int ty, int tw, int th)
-			throws IOException {
-
-		int subencoding = rfb.readU8();
-		if (rfb.rec != null) {
-			rfb.rec.writeByte(subencoding);
-		}
-
-		// Is it a raw-encoded sub-rectangle?
-		if ((subencoding & rfb.HextileRaw) != 0) {
-			handleRawRect(tx, ty, tw, th, false);
-			return;
-		}
-
-		// Read and draw the background if specified.
-		byte[] cbuf = new byte[bytesPixel];
-		if ((subencoding & rfb.HextileBackgroundSpecified) != 0) {
-			rfb.readFully(cbuf);
-			if (bytesPixel == 1) {
-				hextile_bg = colors[cbuf[0] & 0xFF];
-			} else {
-				hextile_bg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF,
-						cbuf[0] & 0xFF);
-			}
-			if (rfb.rec != null) {
-				rfb.rec.write(cbuf);
-			}
-		}
-		memGraphics.setColor(hextile_bg);
-		memGraphics.fillRect(tx, ty, tw, th);
-
-		// Read the foreground color if specified.
-		if ((subencoding & rfb.HextileForegroundSpecified) != 0) {
-			rfb.readFully(cbuf);
-			if (bytesPixel == 1) {
-				hextile_fg = colors[cbuf[0] & 0xFF];
-			} else {
-				hextile_fg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF,
-						cbuf[0] & 0xFF);
-			}
-			if (rfb.rec != null) {
-				rfb.rec.write(cbuf);
-			}
-		}
-
-		// Done with this tile if there is no sub-rectangles.
-		if ((subencoding & rfb.HextileAnySubrects) == 0)
-			return;
-
-		int nSubrects = rfb.readU8();
-		int bufsize = nSubrects * 2;
-		if ((subencoding & rfb.HextileSubrectsColoured) != 0) {
-			bufsize += nSubrects * bytesPixel;
-		}
-		byte[] buf = new byte[bufsize];
-		rfb.readFully(buf);
-		if (rfb.rec != null) {
-			rfb.rec.writeByte(nSubrects);
-			rfb.rec.write(buf);
-		}
-
-		int b1, b2, sx, sy, sw, sh;
-		int i = 0;
-
-		if ((subencoding & rfb.HextileSubrectsColoured) == 0) {
-
-			// Sub-rectangles are all of the same color.
-			memGraphics.setColor(hextile_fg);
-			for (int j = 0; j < nSubrects; j++) {
-				b1 = buf[i++] & 0xFF;
-				b2 = buf[i++] & 0xFF;
-				sx = tx + (b1 >> 4);
-				sy = ty + (b1 & 0xf);
-				sw = (b2 >> 4) + 1;
-				sh = (b2 & 0xf) + 1;
-				memGraphics.fillRect(sx, sy, sw, sh);
-			}
-		} else if (bytesPixel == 1) {
-
-			// BGR233 (8-bit color) version for colored sub-rectangles.
-			for (int j = 0; j < nSubrects; j++) {
-				hextile_fg = colors[buf[i++] & 0xFF];
-				b1 = buf[i++] & 0xFF;
-				b2 = buf[i++] & 0xFF;
-				sx = tx + (b1 >> 4);
-				sy = ty + (b1 & 0xf);
-				sw = (b2 >> 4) + 1;
-				sh = (b2 & 0xf) + 1;
-				memGraphics.setColor(hextile_fg);
-				memGraphics.fillRect(sx, sy, sw, sh);
-			}
-
-		} else {
-
-			// Full-color (24-bit) version for colored sub-rectangles.
-			for (int j = 0; j < nSubrects; j++) {
-				hextile_fg = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF,
-						buf[i] & 0xFF);
-				i += 4;
-				b1 = buf[i++] & 0xFF;
-				b2 = buf[i++] & 0xFF;
-				sx = tx + (b1 >> 4);
-				sy = ty + (b1 & 0xf);
-				sw = (b2 >> 4) + 1;
-				sh = (b2 & 0xf) + 1;
-				memGraphics.setColor(hextile_fg);
-				memGraphics.fillRect(sx, sy, sw, sh);
-			}
-
-		}
-	}
-
-	//
-	// 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 {
-
-		if (zrleInStream == null)
-			zrleInStream = new ZlibInStream();
-//		System.out.println("zrleInStream.end="+zrleInStream.inflater.off);			
-		
-		int nBytes = rfb.readU32();
-		if (nBytes > 64 * 1024 * 1024)
-			throw new Exception("ZRLE decoder: illegal compressed data size");
-
-		if (zrleBuf == null || zrleBufLen < nBytes) {
-			zrleBufLen = nBytes + 4096;
-			zrleBuf = new byte[zrleBufLen];
-		}
-
-		// FIXME: Do not wait for all the data before decompression.
-		rfb.readFully(zrleBuf, 0, nBytes);
-
-		if (rfb.rec != null) {
-			if (rfb.recordFromBeginning) {
-				rfb.rec.writeIntBE(nBytes);
-				rfb.rec.write(zrleBuf, 0, nBytes);
-			} else if (!zrleRecWarningShown) {
-				System.out.println("Warning: ZRLE session can be recorded"
-						+ " only from the beginning");
-				System.out.println("Warning: Recorded file may be corrupted");
-				zrleRecWarningShown = true;
-			}
-
-		}
-
-		zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes);
-
-		for (int ty = y; ty < y + h; ty += 64) {
-
-			int th = Math.min(y + h - ty, 64);
-
-			for (int tx = x; tx < x + w; tx += 64) {
-
-				int tw = Math.min(x + w - tx, 64);
-
-				int mode = zrleInStream.readU8();
-				boolean rle = (mode & 128) != 0;
-				int palSize = mode & 127;
-				int[] palette = new int[128];
-
-				readZrlePalette(palette, palSize);
-
-				if (palSize == 1) {
-					int pix = palette[0];
-					Color c = (bytesPixel == 1) ? colors[pix] : new Color(
-							0xFF000000 | pix);
-					memGraphics.setColor(c);
-					memGraphics.fillRect(tx, ty, tw, th);
-					continue;
-				}
-
-				if (!rle) {
-					if (palSize == 0) {
-						readZrleRawPixels(tw, th);
-					} else {
-						readZrlePackedPixels(tw, th, palette, palSize);
-					}
-				} else {
-					if (palSize == 0) {
-						readZrlePlainRLEPixels(tw, th);
-					} else {
-						readZrlePackedRLEPixels(tw, th, palette);
-					}
-				}
-				handleUpdatedZrleTile(tx, ty, tw, th);
-			}
-		}
-
-		zrleInStream.reset();
-
-		scheduleRepaint(x, y, w, h);
-	}
-
-	int readPixel(InStream is) throws Exception {
-		int pix;
-
-		if (bytesPixel == 1) {
-
-			pix = is.readU8();
-		} else {
-			int p1 = is.readU8();
-			int p2 = is.readU8();
-			int p3 = is.readU8();
-			pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF);
-		}
-		return pix;
-	}
-
-	void readPixels(InStream is, int[] dst, int count) throws Exception {
-		int pix;
-		if (bytesPixel == 1) {
-			byte[] buf = new byte[count];
-			is.readBytes(buf, 0, count);
-			for (int i = 0; i < count; i++) {
-				dst[i] = (int) buf[i] & 0xFF;
-			}
-		} else {
-			byte[] buf = new byte[count * 3];
-			is.readBytes(buf, 0, count * 3);
-			for (int i = 0; i < count; i++) {
-				dst[i] = ((buf[i * 3 + 2] & 0xFF) << 16
-						| (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3] & 0xFF));
-			}
-		}
-	}
-
-	void readZrlePalette(int[] palette, int palSize) throws Exception {
-		readPixels(zrleInStream, palette, palSize);
-	}
-
-	void readZrleRawPixels(int tw, int th) throws Exception {
-		if (bytesPixel == 1) {
-			zrleInStream.readBytes(zrleTilePixels8, 0, tw * th);
-		} else {
-			readPixels(zrleInStream, zrleTilePixels24, tw * th); // /
-		}
-	}
-
-	void readZrlePackedPixels(int tw, int th, int[] palette, int palSize)
-			throws Exception {
-
-		int bppp = ((palSize > 16) ? 8 : ((palSize > 4) ? 4
-				: ((palSize > 2) ? 2 : 1)));
-		int ptr = 0;
-
-		for (int i = 0; i < th; i++) {
-			int eol = ptr + tw;
-			int b = 0;
-			int nbits = 0;
-
-			while (ptr < eol) {
-				if (nbits == 0) {
-					b = zrleInStream.readU8();
-					nbits = 8;
-				}
-				nbits -= bppp;
-				int index = (b >> nbits) & ((1 << bppp) - 1) & 127;
-				if (bytesPixel == 1) {
-					zrleTilePixels8[ptr++] = (byte) palette[index];
-				} else {
-					zrleTilePixels24[ptr++] = palette[index];
-				}
-			}
-		}
-	}
-
-	void readZrlePlainRLEPixels(int tw, int th) throws Exception {
-		int ptr = 0;
-		int end = ptr + tw * th;
-		while (ptr < end) {
-			int pix = readPixel(zrleInStream);
-			int len = 1;
-			int b;
-			do {
-				b = zrleInStream.readU8();
-				len += b;
-			} while (b == 255);
-
-			if (!(len <= end - ptr))
-				throw new Exception("ZRLE decoder: assertion failed"
-						+ " (len <= end-ptr)");
-
-			if (bytesPixel == 1) {
-				while (len-- > 0)
-					zrleTilePixels8[ptr++] = (byte) pix;
-			} else {
-				while (len-- > 0)
-					zrleTilePixels24[ptr++] = pix;
-			}
-		}
-	}
-
-	void readZrlePackedRLEPixels(int tw, int th, int[] palette)
-			throws Exception {
-
-		int ptr = 0;
-		int end = ptr + tw * th;
-		while (ptr < end) {
-			int index = zrleInStream.readU8();
-			int len = 1;
-			if ((index & 128) != 0) {
-				int b;
-				do {
-					b = zrleInStream.readU8();
-					len += b;
-				} while (b == 255);
-
-				if (!(len <= end - ptr))
-					throw new Exception("ZRLE decoder: assertion failed"
-							+ " (len <= end - ptr)");
-			}
-
-			index &= 127;
-			int pix = palette[index];
-
-			if (bytesPixel == 1) {
-				while (len-- > 0)
-					zrleTilePixels8[ptr++] = (byte) pix;
-			} else {
-				while (len-- > 0)
-					zrleTilePixels24[ptr++] = pix;
-			}
-		}
-	}
-
-	//
-	// Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update.
-	//
-
-	void handleUpdatedZrleTile(int x, int y, int w, int h) {
-		Object src, dst;
-		if (bytesPixel == 1) {
-			src = zrleTilePixels8;
-			dst = pixels8;
-		} else {
-			src = zrleTilePixels24;
-			dst = pixels24;
-		}
-		int offsetSrc = 0;
-		int offsetDst = (y * rfb.framebufferWidth + x);
-		for (int j = 0; j < h; j++) {
-			System.arraycopy(src, offsetSrc, dst, offsetDst, w);
-			offsetSrc += w;
-			offsetDst += rfb.framebufferWidth;
-		}
-		handleUpdatedPixels(x, y, w, h);
-	}
-
-	//
-	// Handle a Zlib-encoded rectangle.
-	//
-
-	void handleZlibRect(int x, int y, int w, int h) throws Exception {
-
-		int nBytes = rfb.readU32();
-
-		if (zlibBuf == null || zlibBufLen < nBytes) {
-			zlibBufLen = nBytes * 2;
-			zlibBuf = new byte[zlibBufLen];
-		}
-
-		rfb.readFully(zlibBuf, 0, nBytes);
-
-		if (rfb.rec != null && rfb.recordFromBeginning) {
-			rfb.rec.writeIntBE(nBytes);
-			rfb.rec.write(zlibBuf, 0, nBytes);
-		}
-
-		if (zlibInflater == null) {
-			zlibInflater = new Inflater();
-		}
-		zlibInflater.setInput(zlibBuf, 0, nBytes);
-
-		if (bytesPixel == 1) {
-			for (int dy = y; dy < y + h; dy++) {
-				zlibInflater.inflate(pixels8, dy * rfb.framebufferWidth + x, w);
-				if (rfb.rec != null && !rfb.recordFromBeginning)
-					rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w);
-			}
-		} else {
-			byte[] buf = new byte[w * 4];
-			int i, offset;
-			for (int dy = y; dy < y + h; dy++) {
-				zlibInflater.inflate(buf);
-				offset = dy * rfb.framebufferWidth + x;
-				for (i = 0; i < w; i++) {
-					pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16
-							| (buf[i * 4 + 1] & 0xFF) << 8
-							| (buf[i * 4] & 0xFF);
-				}
-				if (rfb.rec != null && !rfb.recordFromBeginning)
-					rfb.rec.write(buf);
-			}
-		}
-
-		handleUpdatedPixels(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 {
-
-		int comp_ctl = rfb.readU8();
-		if (rfb.rec != null) {
-			if (rfb.recordFromBeginning || comp_ctl == (rfb.TightFill << 4)
-					|| comp_ctl == (rfb.TightJpeg << 4)) {
-				// Send data exactly as received.
-				rfb.rec.writeByte(comp_ctl);
-			} else {
-				// Tell the decoder to flush each of the four zlib streams.
-				rfb.rec.writeByte(comp_ctl | 0x0F);
-			}
-		}
-
-		// Flush zlib streams if we are told by the server to do so.
-		for (int stream_id = 0; stream_id < 4; stream_id++) {
-			if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) {
-				tightInflaters[stream_id] = null;
-			}
-			comp_ctl >>= 1;
-		}
-
-		// Check correctness of subencoding value.
-		if (comp_ctl > rfb.TightMaxSubencoding) {
-			throw new Exception("Incorrect tight subencoding: " + comp_ctl);
-		}
-
-		// Handle solid-color rectangles.
-		if (comp_ctl == rfb.TightFill) {
-
-			if (bytesPixel == 1) {
-				int idx = rfb.readU8();
-				memGraphics.setColor(colors[idx]);
-				if (rfb.rec != null) {
-					rfb.rec.writeByte(idx);
-				}
-			} else {
-				byte[] buf = new byte[3];
-				rfb.readFully(buf);
-				if (rfb.rec != null) {
-					rfb.rec.write(buf);
-				}
-				Color bg = new Color(0xFF000000 | (buf[0] & 0xFF) << 16
-						| (buf[1] & 0xFF) << 8 | (buf[2] & 0xFF));
-				memGraphics.setColor(bg);
-			}
-			memGraphics.fillRect(x, y, w, h);
-			scheduleRepaint(x, y, w, h);
-			return;
-
-		}
-
-		if (comp_ctl == rfb.TightJpeg) {
-
-			statNumRectsTightJPEG++;
-
-			// Read JPEG data.
-			byte[] jpegData = new byte[rfb.readCompactLen()];
-			rfb.readFully(jpegData);
-			if (rfb.rec != null) {
-				if (!rfb.recordFromBeginning) {
-					rfb.recordCompactLen(jpegData.length);
-				}
-				rfb.rec.write(jpegData);
-			}
-
-			// Create an Image object from the JPEG data.
-			Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData);
-
-			// Remember the rectangle where the image should be drawn.
-			jpegRect = new Rectangle(x, y, w, h);
-
-			// Let the imageUpdate() method do the actual drawing, here just
-			// wait until the image is fully loaded and drawn.
-			synchronized (jpegRect) {
-				Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1,
-						this);
-				try {
-					// Wait no longer than three seconds.
-					jpegRect.wait(3000);
-				} catch (InterruptedException e) {
-					throw new Exception("Interrupted while decoding JPEG image");
-				}
-			}
-
-			// Done, jpegRect is not needed any more.
-			jpegRect = null;
-			return;
-
-		}
-
-		// Read filter id and parameters.
-		int numColors = 0, rowSize = w;
-		byte[] palette8 = new byte[2];
-		int[] palette24 = new int[256];
-		boolean useGradient = false;
-		if ((comp_ctl & rfb.TightExplicitFilter) != 0) {
-			int filter_id = rfb.readU8();
-			if (rfb.rec != null) {
-				rfb.rec.writeByte(filter_id);
-			}
-			if (filter_id == rfb.TightFilterPalette) {
-				numColors = rfb.readU8() + 1;
-				if (rfb.rec != null) {
-					rfb.rec.writeByte(numColors - 1);
-				}
-				if (bytesPixel == 1) {
-					if (numColors != 2) {
-						throw new Exception("Incorrect tight palette size: "
-								+ numColors);
-					}
-					rfb.readFully(palette8);
-					if (rfb.rec != null) {
-						rfb.rec.write(palette8);
-					}
-				} else {
-					byte[] buf = new byte[numColors * 3];
-					rfb.readFully(buf);
-					if (rfb.rec != null) {
-						rfb.rec.write(buf);
-					}
-					for (int i = 0; i < numColors; i++) {
-						palette24[i] = ((buf[i * 3] & 0xFF) << 16
-								| (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3 + 2] & 0xFF));
-					}
-				}
-				if (numColors == 2)
-					rowSize = (w + 7) / 8;
-			} else if (filter_id == rfb.TightFilterGradient) {
-				useGradient = true;
-			} else if (filter_id != rfb.TightFilterCopy) {
-				throw new Exception("Incorrect tight filter id: " + filter_id);
-			}
-		}
-		if (numColors == 0 && bytesPixel == 4)
-			rowSize *= 3;
-
-		// Read, optionally uncompress and decode data.
-		int dataSize = h * rowSize;
-		if (dataSize < rfb.TightMinToCompress) {
-			// Data size is small - not compressed with zlib.
-			if (numColors != 0) {
-				// Indexed colors.
-				byte[] indexedData = new byte[dataSize];
-				rfb.readFully(indexedData);
-				if (rfb.rec != null) {
-					rfb.rec.write(indexedData);
-				}
-				if (numColors == 2) {
-					// Two colors.
-					if (bytesPixel == 1) {
-						decodeMonoData(x, y, w, h, indexedData, palette8);
-					} else {
-						decodeMonoData(x, y, w, h, indexedData, palette24);
-					}
-				} else {
-					// 3..255 colors (assuming bytesPixel == 4).
-					int i = 0;
-					for (int dy = y; dy < y + h; dy++) {
-						for (int dx = x; dx < x + w; dx++) {
-							pixels24[dy * rfb.framebufferWidth + dx] = palette24[indexedData[i++] & 0xFF];
-						}
-					}
-				}
-			} else if (useGradient) {
-				// "Gradient"-processed data
-				byte[] buf = new byte[w * h * 3];
-				rfb.readFully(buf);
-				if (rfb.rec != null) {
-					rfb.rec.write(buf);
-				}
-				decodeGradientData(x, y, w, h, buf);
-			} else {
-				// Raw truecolor data.
-				if (bytesPixel == 1) {
-					for (int dy = y; dy < y + h; dy++) {
-						rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w);
-						if (rfb.rec != null) {
-							rfb.rec.write(pixels8, dy * rfb.framebufferWidth
-									+ x, w);
-						}
-					}
-				} else {
-					byte[] buf = new byte[w * 3];
-					int i, offset;
-					for (int dy = y; dy < y + h; dy++) {
-						rfb.readFully(buf);
-						if (rfb.rec != null) {
-							rfb.rec.write(buf);
-						}
-						offset = dy * rfb.framebufferWidth + x;
-						for (i = 0; i < w; i++) {
-							pixels24[offset + i] = (buf[i * 3] & 0xFF) << 16
-									| (buf[i * 3 + 1] & 0xFF) << 8
-									| (buf[i * 3 + 2] & 0xFF);
-						}
-					}
-				}
-			}
-		} else {
-			// Data was compressed with zlib.
-			int zlibDataLen = rfb.readCompactLen();
-			byte[] zlibData = new byte[zlibDataLen];
-			rfb.readFully(zlibData);
-			if (rfb.rec != null && rfb.recordFromBeginning) {
-				rfb.rec.write(zlibData);
-			}
-			int stream_id = comp_ctl & 0x03;
-			if (tightInflaters[stream_id] == null) {
-				tightInflaters[stream_id] = new Inflater();
-			}
-			Inflater myInflater = tightInflaters[stream_id];
-			myInflater.setInput(zlibData);
-			byte[] buf = new byte[dataSize];
-			myInflater.inflate(buf);
-			if (rfb.rec != null && !rfb.recordFromBeginning) {
-				rfb.recordCompressedData(buf);
-			}
-
-			if (numColors != 0) {
-				// Indexed colors.
-				if (numColors == 2) {
-					// Two colors.
-					if (bytesPixel == 1) {
-						decodeMonoData(x, y, w, h, buf, palette8);
-					} else {
-						decodeMonoData(x, y, w, h, buf, palette24);
-					}
-				} else {
-					// More than two colors (assuming bytesPixel == 4).
-					int i = 0;
-					for (int dy = y; dy < y + h; dy++) {
-						for (int dx = x; dx < x + w; dx++) {
-							pixels24[dy * rfb.framebufferWidth + dx] = palette24[buf[i++] & 0xFF];
-						}
-					}
-				}
-			} else if (useGradient) {
-				// Compressed "Gradient"-filtered data (assuming bytesPixel ==
-				// 4).
-				decodeGradientData(x, y, w, h, buf);
-			} else {
-				// Compressed truecolor data.
-				if (bytesPixel == 1) {
-					int destOffset = y * rfb.framebufferWidth + x;
-					for (int dy = 0; dy < h; dy++) {
-						System.arraycopy(buf, dy * w, pixels8, destOffset, w);
-						destOffset += rfb.framebufferWidth;
-					}
-				} else {
-					int srcOffset = 0;
-					int destOffset, i;
-					for (int dy = 0; dy < h; dy++) {
-						myInflater.inflate(buf);
-						destOffset = (y + dy) * rfb.framebufferWidth + x;
-						for (i = 0; i < w; i++) {
-							pixels24[destOffset + i] = (buf[srcOffset] & 0xFF) << 16
-									| (buf[srcOffset + 1] & 0xFF) << 8
-									| (buf[srcOffset + 2] & 0xFF);
-							srcOffset += 3;
-						}
-					}
-				}
-			}
-		}
-
-		handleUpdatedPixels(x, y, w, h);
-		scheduleRepaint(x, y, w, h);
-	}
-
-	//
-	// Decode 1bpp-encoded bi-color rectangle (8-bit and 24-bit versions).
-	//
-
-	void decodeMonoData(int x, int y, int w, int h, byte[] src, byte[] palette) {
-
-		int dx, dy, n;
-		int i = y * rfb.framebufferWidth + x;
-		int rowBytes = (w + 7) / 8;
-		byte b;
-
-		for (dy = 0; dy < h; dy++) {
-			for (dx = 0; dx < w / 8; dx++) {
-				b = src[dy * rowBytes + dx];
-				for (n = 7; n >= 0; n--)
-					pixels8[i++] = palette[b >> n & 1];
-			}
-			for (n = 7; n >= 8 - w % 8; n--) {
-				pixels8[i++] = palette[src[dy * rowBytes + dx] >> n & 1];
-			}
-			i += (rfb.framebufferWidth - w);
-		}
-	}
-
-	void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette) {
-
-		int dx, dy, n;
-		int i = y * rfb.framebufferWidth + x;
-		int rowBytes = (w + 7) / 8;
-		byte b;
-
-		for (dy = 0; dy < h; dy++) {
-			for (dx = 0; dx < w / 8; dx++) {
-				b = src[dy * rowBytes + dx];
-				for (n = 7; n >= 0; n--)
-					pixels24[i++] = palette[b >> n & 1];
-			}
-			for (n = 7; n >= 8 - w % 8; n--) {
-				pixels24[i++] = palette[src[dy * rowBytes + dx] >> n & 1];
-			}
-			i += (rfb.framebufferWidth - w);
-		}
-	}
-
-	//
-	// Decode data processed with the "Gradient" filter.
-	//
-
-	void decodeGradientData(int x, int y, int w, int h, byte[] buf) {
-
-		int dx, dy, c;
-		byte[] prevRow = new byte[w * 3];
-		byte[] thisRow = new byte[w * 3];
-		byte[] pix = new byte[3];
-		int[] est = new int[3];
-
-		int offset = y * rfb.framebufferWidth + x;
-
-		for (dy = 0; dy < h; dy++) {
-
-			/* First pixel in a row */
-			for (c = 0; c < 3; c++) {
-				pix[c] = (byte) (prevRow[c] + buf[dy * w * 3 + c]);
-				thisRow[c] = pix[c];
-			}
-			pixels24[offset++] = (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8
-					| (pix[2] & 0xFF);
-
-			/* Remaining pixels of a row */
-			for (dx = 1; dx < w; dx++) {
-				for (c = 0; c < 3; c++) {
-					est[c] = ((prevRow[dx * 3 + c] & 0xFF) + (pix[c] & 0xFF) - (prevRow[(dx - 1)
-							* 3 + c] & 0xFF));
-					if (est[c] > 0xFF) {
-						est[c] = 0xFF;
-					} else if (est[c] < 0x00) {
-						est[c] = 0x00;
-					}
-					pix[c] = (byte) (est[c] + buf[(dy * w + dx) * 3 + c]);
-					thisRow[dx * 3 + c] = pix[c];
-				}
-				pixels24[offset++] = (pix[0] & 0xFF) << 16
-						| (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
-			}
-
-			System.arraycopy(thisRow, 0, prevRow, 0, w * 3);
-			offset += (rfb.framebufferWidth - w);
-		}
-	}
-
-	//
-	// Display newly updated area of pixels.
-	//
-
-	void handleUpdatedPixels(int x, int y, int w, int h) {
-
-		// Draw updated pixels of the off-screen image.
-		pixelsSource.newPixels(x, y, w, h);
-		memGraphics.setClip(x, y, w, h);
-		memGraphics.drawImage(rawPixelsImage, 0, 0, null);
-		memGraphics.setClip(0, 0, rfb.framebufferWidth, rfb.framebufferHeight);
-	}
-
-	//
-	// Tell JVM to repaint specified desktop area.
-	//
-
-	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);
-	}
-
-	public 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();
-	}
-
-	public void processLocalMouseEvent(MouseEvent evt, boolean moved) {
-		if (viewer.rfb != null && rfb.inNormalProtocol) {
-			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();
-			}
-		}
-	}
-
-	//
-	// Ignored events.
-	//
-
-	public void mouseClicked(MouseEvent evt) {
-	}
-
-	public void mouseEntered(MouseEvent evt) {
-	}
-
-	public void mouseExited(MouseEvent evt) {
-	}
-
-	//
-	// 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;
-	}
-
-	// ////////////////////////////////////////////////////////////////
-	//
-	// Handle cursor shape updates (XCursor and RichCursor encodings).
-	//
-
-	boolean showSoftCursor = false;
-
-	MemoryImageSource softCursorSource;
-	Image softCursor;
-
-	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 * bytesPixel + 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 pixByte, 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);
-		}
-	}
-}
--- a/src/VncCanvas2.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +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.
-//
-
-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) {}
-  }
-
-}
-
--- a/src/VncProxyService.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,765 +0,0 @@
-import java.awt.*;
-import java.awt.event.*;
-import java.io.*;
-import java.net.*;
-
-public class VncProxyService implements java.lang.Runnable {
-
-	public static void main(String[] argv) {
-		VncProxyService v = new VncProxyService();
-		v.mainArgs = argv;
-
-		v.init();
-//		v.start();
-	}
-	
-	String[] mainArgs;
-
-	// RfbProto rfb;
-	MyRfbProto rfb;
-	Thread rfbThread;
-
-	Frame vncFrame;
-	Container vncContainer;
-	ScrollPane desktopScrollPane;
-	GridBagLayout gridbag;
-	ButtonPanel buttonPanel;
-	Label connStatusLabel;
-	ProxyVncCanvas 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;
-
-	//
-	// init()
-	//
-
-	public void init() {
-
-		readParameters();
-
-		recordingSync = new Object();
-
-		sessionFileName = null;
-		recordingActive = false;
-		recordingStatusChanged = false;
-		cursorUpdatesDef = null;
-		eightBitColorsDef = null;
-
-		rfbThread = new Thread(this);
-		rfbThread.start();
-		
-	}
-
-	//
-	// run() - executed by the rfbThread to deal with the RFB socket.
-	//
-
-	public void run() {
-
-		try {
-			connectAndAuthenticate();
-			doProtocolInitialisation();
-
-			vc = new ProxyVncCanvas(this, 0, 0);
-
-			processNormalProtocol();// main loop
-
-		} 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 (rfb != null && !rfb.closed())
-					rfb.close();
-				if (showControls && buttonPanel != null) {
-					buttonPanel.disableButtonsOnDisconnect();
-				}
-			} 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);
-			}
-		}
-
-	}
-
-	//
-	// 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();// main loop
-		} 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...");
-
-		showConnectionStatus("Connecting to " + host + ", port " + port + "...");
-
-		// rfb = new RfbProto(host, port, this);
-		rfb = new MyRfbProto(host, port);
-		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();
-		int authType;
-		if (secType == RfbProto.SecTypeTight) {
-			showConnectionStatus("Enabling TightVNC protocol extensions");
-			rfb.setupTunneling();
-			authType = rfb.negotiateAuthenticationTight();
-		} else {
-			authType = secType;
-		}
-
-		switch (authType) {
-		case RfbProto.AuthNone:
-			showConnectionStatus("No authentication needed");
-			rfb.authenticateNone();
-			break;
-		case RfbProto.AuthVNC:
-			showConnectionStatus("Performing standard VNC authentication");
-			if (passwordParam != null) {
-				rfb.authenticateVNC(passwordParam);
-			} else {
-				String pw = askPassword();
-				rfb.authenticateVNC(pw);
-			}
-			break;
-		default:
-			throw new Exception("Unknown authentication scheme " + authType);
-		}
-	}
-
-	//
-	// Show a message describing the connection status.
-	// To hide the connection status label, use (msg == null).
-	//
-
-	void showConnectionStatus(String msg) {
-		System.out.println(msg);
-	}
-
-	//
-	// Show an authentication panel.
-	//
-
-	String askPassword() throws Exception {
-		showConnectionStatus(null);
-		/*
-		 * AuthPanel authPanel = new AuthPanel(this);
-		 * 
-		 * 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);
-
-
-		authPanel.moveFocusToDefaultField();
-		vncContainer.remove(authPanel);
-		 */
-		String pw = mainArgs[2];
-		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();
-			}
-
-			System.out.println("Recording the session in " + sessionFileName);
-			rfb.startSession(sessionFileName);
-			recordingActive = true;
-		}
-	}
-
-	//
-	// 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() {
-
-		if (mainArgs.length > 0)
-			host = mainArgs[0];
-		else
-			host = "hades.cr.ie.u-ryukyu.ac.jp";
-
-		if (mainArgs.length > 1)
-			port = Integer.parseInt(mainArgs[1]);
-		else
-			port = 5900;
-
-		// Read "ENCPASSWORD" or "PASSWORD" parameter if specified.
-//		readPasswordParameters();
-
-		String str;
-
-		// "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);
-		String encPasswordParam = mainArgs[2];
-
-		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) {
-		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;
-	}
-
-	//
-	// 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;
-			int nRealRects = vc.statNumPixelRects;
-			int nPseudoRects = vc.statNumTotalRects - vc.statNumPixelRects;
-			System.out.println("Updates received: " + vc.statNumUpdates + " ("
-					+ nRealRects + " rectangles + " + nPseudoRects
-					+ " pseudo), " + rate + " updates/sec");
-			int 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);
-
-			int raw = vc.statNumBytesDecoded;
-			int 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();
-
-		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);
-		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();
-
-		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);
-			 */
-		}
-
-	}
-
-	//
-	// 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();
-	}
-
-	//
-	// Start/stop receiving mouse events.
-	//
-
-	public void enableInput(boolean enable) {
-		vc.enableInput(enable);
-	}
-
-	//
-	// Close application properly on window close event.
-	//
-
-	public void windowClosing(WindowEvent evt) {
-		System.out.println("Closing window");
-		if (rfb != null)
-			disconnect();
-
-		vncContainer.hide();
-
-	}
-
-	//
-	// 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) {
-	}
-}
--- a/src/VncViewer.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1039 +0,0 @@
-//
-//  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.
-//
-
-//
-// 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.
-//
-
-import java.awt.*;
-import java.awt.event.*;
-import java.io.*;
-import java.net.*;
-
-public class VncViewer extends java.applet.Applet implements
-		java.lang.Runnable, WindowListener {
-
-	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 static void main(String[] argv) {
-		VncViewer v = new VncViewer();
-		v.mainArgs = argv;
-		v.inAnApplet = false;
-		v.inSeparateFrame = true;
-		/*
-		 * if(argv.length > 1){ v.host = argv[0]; v.port =
-		 * Integer.parseInt(argv[1]); }
-		 */
-		v.init();
-		v.start();
-	}
-
-	String[] mainArgs;
-
-	// RfbProto rfb;
-	MyRfbProto 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("TightVNC");
-			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);
-
-		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);
-			} 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);
-
-				// 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();
-
-			}
-
-			if (showControls)
-				buttonPanel.enableButtons();
-
-			moveFocusToDesktop();
-			processNormalProtocol();// main loop
-
-		} 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);
-			}
-		}
-
-	}
-
-	//
-	// 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("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();// main loop
-		} 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.setVisible(true);
-		} else {
-			validate();
-		}
-
-		showConnectionStatus("Connecting to " + host + ", port " + port + "...");
-
-		// rfb = new RfbProto(host, port, this);
-		rfb = new MyRfbProto(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();
-		int authType;
-		if (secType == RfbProto.SecTypeTight) {
-			showConnectionStatus("Enabling TightVNC protocol extensions");
-			rfb.setupTunneling();
-			authType = rfb.negotiateAuthenticationTight();
-		} else {
-			authType = secType;
-		}
-
-		switch (authType) {
-		case RfbProto.AuthNone:
-			showConnectionStatus("No authentication needed");
-			rfb.authenticateNone();
-			break;
-		case RfbProto.AuthVNC:
-			showConnectionStatus("Performing standard VNC authentication");
-			if (passwordParam != null) {
-				rfb.authenticateVNC(passwordParam);
-			} else {
-				String pw = askPassword();
-				rfb.authenticateVNC(pw);
-			}
-			break;
-		default:
-			throw new Exception("Unknown authentication scheme " + authType);
-		}
-	}
-
-	//
-	// 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 askPassword() throws Exception {
-		showConnectionStatus(null);
-
-		AuthPanel authPanel = new AuthPanel(this);
-
-		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();
-
-/*
-		if (rfb.MYVNC) {
-			rfb.initServSock(5550);
-
-			try {
-				Socket newCli = rfb.accept();
-				rfb.sendInitData(newCli);
-				rfb.addSock(newCli);
-			} catch (IOException e) {
-			}
-
-		}
-*/
-
-		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();
-			}
-
-			System.out.println("Recording the session in " + sessionFileName);
-			rfb.startSession(sessionFileName);
-			recordingActive = true;
-		}
-	}
-
-	//
-	// 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 (mainArgs.length > 0)
-			host = mainArgs[0];
-		else
-			host = "hades.cr.ie.u-ryukyu.ac.jp";
-		/*
-		 * if (host == null) { host = getCodeBase().getHost(); if
-		 * (host.equals("")) { fatalError("HOST parameter not specified"); } }
-		 */
-
-		// port = readIntParameter("PORT", 5900);
-		if (mainArgs.length > 1)
-			port = Integer.parseInt(mainArgs[1]);
-		else
-			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;
-			int nRealRects = vc.statNumPixelRects;
-			int nPseudoRects = vc.statNumTotalRects - vc.statNumPixelRects;
-			System.out.println("Updates received: " + vc.statNumUpdates + " ("
-					+ nRealRects + " rectangles + " + nPseudoRects
-					+ " pseudo), " + rate + " updates/sec");
-			int 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);
-
-			int raw = vc.statNumBytesDecoded;
-			int 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);
-	}
-
-	//
-	// Close application properly on window close event.
-	//
-
-	public void windowClosing(WindowEvent evt) {
-		System.out.println("Closing window");
-		if (rfb != null)
-			disconnect();
-
-		vncContainer.hide();
-
-		if (!inAnApplet) {
-			System.exit(0);
-		}
-	}
-
-	//
-	// 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) {
-	}
-}
--- a/src/ZlibInStream.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +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
-//
-
-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;
-}
--- a/src/acceptThread.java	Sun Apr 24 16:48:39 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-import java.net.BindException;
-import java.net.Socket;
-import java.io.IOException;
-
-public class acceptThread implements Runnable {
-	MyRfbProto rfb;
-
-	acceptThread(MyRfbProto _rfb) {
-		rfb = _rfb;
-	}
-
-
-	public void run() {
-		rfb.selectPort();
-		
-		while (true) {
-			try {
-				Socket newCli = rfb.accept();
-				rfb.sendInitData(newCli);
-				rfb.addSock(newCli);
-			} catch (IOException e) {
-
-			}
-		}
-	}
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/AuthPanel.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,117 @@
+package myVncProxy;
+//
+//  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.
+//
+
+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;
+
+  //
+  // Constructor.
+  //
+
+  public AuthPanel(VncViewer viewer)
+  {
+    Label titleLabel = new Label("VNC Authentication", Label.CENTER);
+    titleLabel.setFont(new Font("Helvetica", Font.BOLD, 18));
+
+    Label promptLabel = new Label("Password:", Label.CENTER);
+
+    passwordField = new TextField(10);
+    passwordField.setForeground(Color.black);
+    passwordField.setBackground(Color.white);
+    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();
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/ButtonPanel.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,155 @@
+package myVncProxy;
+//
+//  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.
+//
+
+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);
+
+    validate();
+  }
+
+  //
+  // 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();
+      }
+    }
+  }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/CapabilityInfo.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,88 @@
+package myVncProxy;
+//
+//  Copyright (C) 2003 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.
+//
+
+//
+// CapabilityInfo.java - A class to hold information about a
+// particular capability as used in the RFB protocol 3.130.
+//
+
+class CapabilityInfo {
+
+  // Public methods
+
+  public CapabilityInfo(int code,
+			String vendorSignature,
+			String nameSignature,
+			String description) {
+    this.code = code;
+    this.vendorSignature = vendorSignature;
+    this.nameSignature = nameSignature;
+    this.description = description;
+    enabled = false;
+  }
+
+  public CapabilityInfo(int code,
+			byte[] vendorSignature,
+			byte[] nameSignature) {
+    this.code = code;
+    this.vendorSignature = new String(vendorSignature);
+    this.nameSignature = new String(nameSignature);
+    this.description = null;
+    enabled = false;
+  }
+
+  public int getCode() {
+    return code;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public boolean isEnabled() {
+    return enabled;
+  }
+
+  public void enable() {
+    enabled = true;
+  }
+
+  public boolean equals(CapabilityInfo other) {
+    return (other != null && this.code == other.code &&
+	    this.vendorSignature.equals(other.vendorSignature) &&
+	    this.nameSignature.equals(other.nameSignature));
+  }
+
+  public boolean enableIfEquals(CapabilityInfo other) {
+    if (this.equals(other))
+      enable();
+
+    return isEnabled();
+  }
+
+  // Protected data
+
+  protected int code;
+  protected String vendorSignature;
+  protected String nameSignature;
+
+  protected String description;
+  protected boolean enabled;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/CapsContainer.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,104 @@
+package myVncProxy;
+//
+//  Copyright (C) 2003 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.
+//
+
+//
+// CapsContainer.java - A container of capabilities as used in the RFB
+// protocol 3.130
+//
+
+import java.util.Vector;
+import java.util.Hashtable;
+
+class CapsContainer {
+
+  // Public methods
+
+  public CapsContainer() {
+    infoMap = new Hashtable(64, (float)0.25);
+    orderedList = new Vector(32, 8);
+  }
+
+  public void add(CapabilityInfo capinfo) {
+    Integer key = new Integer(capinfo.getCode());
+    infoMap.put(key, capinfo);
+  }
+
+  public void add(int code, String vendor, String name, String desc) {
+    Integer key = new Integer(code);
+    infoMap.put(key, new CapabilityInfo(code, vendor, name, desc));
+  }
+
+  public boolean isKnown(int code) {
+    return infoMap.containsKey(new Integer(code));
+  }
+
+  public CapabilityInfo getInfo(int code) {
+    return (CapabilityInfo)infoMap.get(new Integer(code));
+  }
+
+  public String getDescription(int code) {
+    CapabilityInfo capinfo = (CapabilityInfo)infoMap.get(new Integer(code));
+    if (capinfo == null)
+      return null;
+
+    return capinfo.getDescription();
+  }
+
+  public boolean enable(CapabilityInfo other) {
+    Integer key = new Integer(other.getCode());
+    CapabilityInfo capinfo = (CapabilityInfo)infoMap.get(key);
+    if (capinfo == null)
+      return false;
+
+    boolean enabled = capinfo.enableIfEquals(other);
+    if (enabled)
+      orderedList.addElement(key);
+
+    return enabled;
+  }
+
+  public boolean isEnabled(int code) {
+    CapabilityInfo capinfo = (CapabilityInfo)infoMap.get(new Integer(code));
+    if (capinfo == null)
+      return false;
+
+    return capinfo.isEnabled();
+  }
+
+  public int numEnabled() {
+    return orderedList.size();
+  }
+
+  public int getByOrder(int idx) {
+    int code;
+    try {
+      code = ((Integer)orderedList.elementAt(idx)).intValue();
+    } catch (ArrayIndexOutOfBoundsException e) {
+      code = 0;
+    }
+    return code;
+  }
+
+  // Protected data
+
+  protected Hashtable infoMap;
+  protected Vector orderedList;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/ClipboardFrame.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,134 @@
+package myVncProxy;
+//
+//  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.
+//
+
+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("TightVNC 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);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/DesCipher.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,497 @@
+package myVncProxy;
+//
+// 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/
+
+
+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];
+	    }
+        }
+    }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/HTTPConnectSocket.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,60 @@
+package myVncProxy;
+//
+//  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.
+//
+
+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);
+  }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/HTTPConnectSocketFactory.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,87 @@
+package myVncProxy;
+//
+//  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.
+//
+
+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;
+  }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/InStream.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,178 @@
+package myVncProxy;
+/* 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).
+//
+
+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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/MemInStream.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,33 @@
+package myVncProxy;
+/* 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.
+ */
+
+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");
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/MyRfbProto.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,206 @@
+package myVncProxy;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.BindException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.LinkedList;
+
+
+class MyRfbProto extends RfbProto {
+
+	private int messageType;
+	private int rectangles;
+	private int rectX;
+	private int rectY;
+	private int rectW;
+	private int rectH;
+	private int encoding;
+
+	private ServerSocket servSock;
+	private int acceptPort;
+	private byte initData[];
+	private LinkedList <Socket> cliList;
+	
+	MyRfbProto(String h, int p, VncViewer v ) throws IOException {
+		super(h, p, v);
+		cliList = new LinkedList <Socket>();
+	}
+
+	MyRfbProto(String h, int p) throws IOException {
+		super(h, p);
+		cliList = new LinkedList <Socket>();
+	}
+
+	void initServSock(int port) throws IOException{
+		servSock = new ServerSocket(port);
+		acceptPort = port;
+	}
+	void selectPort(){
+		int i = 5550;
+		while(true){
+			try{
+				initServSock(i);
+				break;
+			}catch(BindException e){
+				i++;
+				continue;
+			}catch(IOException e){
+
+			}
+		}
+	}
+	int getAcceptPort(){
+		return acceptPort;
+	}
+	void setSoTimeout(int num) throws IOException {
+		servSock.setSoTimeout(num);
+	}
+	
+	Socket accept() throws IOException {
+		return servSock.accept();
+	}
+
+	void addSock(Socket sock){
+		cliList.add(sock);
+	}
+	
+	void mark(int len) throws IOException {
+		is.mark(len);
+	}
+
+	void reset() throws IOException {
+		is.reset();
+ 	}
+
+	boolean markSupported() {
+		return is.markSupported();
+	}
+	
+	void readServerInit() throws IOException {
+		
+		mark(255);
+		skipBytes(20);
+		int nlen = readU32();
+		int blen = 20+4+nlen;
+		initData = new byte[blen];
+		reset();
+
+		mark(blen);
+		readFully(initData);
+		reset();
+		
+		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);
+
+		// Read interaction capabilities (TightVNC protocol extensions)
+		if (protocolTightVNC) {
+			int nServerMessageTypes = readU16();
+			int nClientMessageTypes = readU16();
+			int nEncodingTypes = readU16();
+			readU16();
+			readCapabilityList(serverMsgCaps, nServerMessageTypes);
+			readCapabilityList(clientMsgCaps, nClientMessageTypes);
+			readCapabilityList(encodingCaps, nEncodingTypes);
+		}
+
+		inNormalProtocol = true;
+	}
+
+	void sendInitData(Socket sock) throws IOException{
+			sock.getOutputStream().write(initData);
+	}
+
+//	void sendData(byte b[]) throws IOException{
+	void sendData(byte b[]){
+		try{
+			for(Socket cli : cliList){
+				try{
+					cli.getOutputStream().write(b, 0, b.length);
+				}catch(IOException e){
+					// if socket closed
+					//				cliList.remove(cli);
+					cliList.remove(cli);
+				}
+			}
+//		System.out.println("cliSize="+cliSize());
+		}catch(Exception e){
+//			System.out.println("cliSize 0");
+		}
+	}	
+	boolean ready() throws IOException {
+		BufferedReader br = new BufferedReader(new InputStreamReader(is));
+		return br.ready();
+	}	
+
+	int cliSize(){
+		return cliList.size();
+	}	
+	void printNumBytesRead(){
+		System.out.println("numBytesRead="+numBytesRead);
+	}	
+	void bufResetSend(int size) throws IOException {
+		reset();
+		int len = size;
+		if(available() < size )
+			len = available();
+		byte buffer[] = new byte[len];
+		readFully(buffer);
+		sendData(buffer);
+	}
+	void regiFramebufferUpdate()throws IOException{
+		mark(16);
+		messageType = readU8();
+		skipBytes(1);
+		rectangles = readU16();
+		rectX = readU16();
+		rectY = readU16();
+		rectW = readU16();
+		rectH = readU16();
+		encoding = readU32();	
+		reset();
+	}
+	void checkAndMark() throws IOException{
+		switch(encoding){
+		case RfbProto.EncodingRaw:
+			mark(rectW * rectH * 4 + 16);		
+			break;
+		default:
+			mark(1000000);
+		}
+	}
+	
+	void printFramebufferUpdate(){
+	
+		System.out.println("messageType=" + messageType);
+		System.out.println("rectangles="+rectangles);
+		System.out.println("encoding=" + encoding);
+		switch(encoding){
+		case RfbProto.EncodingRaw:
+			System.out.println("rectW * rectH * 4 + 16 =" + rectW * rectH * 4 + 16);
+			break;
+		default:
+			
+		
+		}
+	}
+	
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/OptionsFrame.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,406 @@
+package myVncProxy;
+//
+//  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.
+//
+
+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",
+    "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" },
+    { "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,
+    scaleCursorIndex     = 8,
+    shareDesktopIndex    = 9;
+
+  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("TightVNC 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);
+	  }
+	}
+      }
+    }
+
+    // FIXME: Provide some sort of GUI for "Scaling Factor".
+
+    autoScale = false;
+    scalingFactor = 100;
+    String s = viewer.readParameter("Scaling Factor", false);
+    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;
+	}
+      }
+    }
+
+    // Make the booleans and encodings array correspond to the state of the GUI
+
+    setEncodings();
+    setColorFormat();
+    setOtherOptions();
+  }
+
+
+  //
+  // 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;
+
+    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;
+    } else if (choices[encodingIndex].getSelectedItem().equals("Auto")) {
+      preferredEncoding = -1;
+    }
+
+    // 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;
+    }
+
+    // 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;
+
+    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();
+
+    }
+  }
+
+  //
+  // 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) {}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/ProxyVncCanvas.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,1878 @@
+package myVncProxy;
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.*;
+import java.io.*;
+import java.lang.*;
+import java.nio.ByteBuffer;
+import java.util.zip.*;
+
+import java.net.Socket;
+
+//
+//VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
+//
+
+class ProxyVncCanvas extends Canvas implements KeyListener, MouseListener,
+		MouseMotionListener {
+
+	VncProxyService viewer;
+	MyRfbProto rfb;
+	ColorModel cm8, cm24;
+	Color[] colors;
+	int bytesPixel;
+
+	int maxWidth = 0, maxHeight = 0;
+	int scalingFactor;
+	int scaledWidth, scaledHeight;
+
+	Image memImage;
+	Graphics memGraphics;
+
+	Image rawPixelsImage;
+	MemoryImageSource pixelsSource;
+	byte[] pixels8;
+	int[] pixels24;
+
+	// Update statistics.
+	long statStartTime; // time on first framebufferUpdateRequest
+	int statNumUpdates; // counter for FramebufferUpdate messages
+	int statNumTotalRects; // rectangles in FramebufferUpdate messages
+	int statNumPixelRects; // the same, but excluding pseudo-rectangles
+	int statNumRectsTight; // Tight-encoded rectangles (including JPEG)
+	int statNumRectsTightJPEG; // JPEG-compressed Tight-encoded rectangles
+	int statNumRectsZRLE; // ZRLE-encoded rectangles
+	int statNumRectsHextile; // Hextile-encoded rectangles
+	int statNumRectsRaw; // Raw-encoded rectangles
+	int statNumRectsCopy; // CopyRect rectangles
+	int statNumBytesEncoded; // number of bytes in updates, as received
+	int statNumBytesDecoded; // number of bytes, as if Raw encoding was used
+
+	// ZRLE encoder's data.
+	byte[] zrleBuf;
+	int zrleBufLen = 0;
+	byte[] zrleTilePixels8;
+	int[] zrleTilePixels24;
+	ZlibInStream zrleInStream;
+	boolean zrleRecWarningShown = false;
+
+	// Zlib encoder's data.
+	byte[] zlibBuf;
+	int zlibBufLen = 0;
+	Inflater zlibInflater;
+
+	// Tight encoder's data.
+	final static int tightZlibBufferSize = 512;
+	Inflater[] tightInflaters;
+
+	// Since JPEG images are loaded asynchronously, we have to remember
+	// their position in the framebuffer. Also, this jpegRect object is
+	// used for synchronization between the rfbThread and a JVM's thread
+	// which decodes and loads JPEG images.
+	Rectangle jpegRect;
+
+	// True if we process keyboard and mouse events.
+	boolean inputEnabled;
+
+	
+	//
+	// The constructors.
+	//
+
+	public ProxyVncCanvas(VncProxyService v, int maxWidth_, int maxHeight_)
+			throws IOException {
+
+		viewer = v;
+		maxWidth = maxWidth_;
+		maxHeight = maxHeight_;
+
+		rfb = viewer.rfb;
+
+		tightInflaters = new Inflater[4];
+
+		cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6));
+		cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
+
+		colors = new Color[256];
+		for (int i = 0; i < 256; i++)
+			colors[i] = new Color(cm8.getRGB(i));
+
+		// setPixelFormat();
+
+		inputEnabled = false;
+		// Keyboard listener is enabled even in view-only mode, to catch
+		// 'r' or 'R' key presses used to request screen update.
+		addKeyListener(this);
+	}
+
+	public ProxyVncCanvas(VncProxyService 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);
+	}
+
+	//
+	// Override the ImageObserver interface method to handle drawing of
+	// JPEG-encoded data.
+	//
+
+	public boolean imageUpdate(Image img, int infoflags, int x, int y,
+			int width, int height) {
+		if ((infoflags & (ALLBITS | ABORT)) == 0) {
+			return true; // We need more image data.
+		} else {
+			// If the whole image is available, draw it now.
+			if ((infoflags & ALLBITS) != 0) {
+				if (jpegRect != null) {
+					synchronized (jpegRect) {
+						memGraphics
+								.drawImage(img, jpegRect.x, jpegRect.y, null);
+						scheduleRepaint(jpegRect.x, jpegRect.y, jpegRect.width,
+								jpegRect.height);
+						jpegRect.notify();
+					}
+				}
+			}
+			return false; // All image data was processed.
+		}
+	}
+
+	//
+	// 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;
+			addMouseListener(this);
+			addMouseMotionListener(this);
+			if (viewer.showControls) {
+				viewer.buttonPanel.enableRemoteAccessControls(true);
+			}
+			createSoftCursor(); // scaled cursor
+		} else if (!enable && inputEnabled) {
+			inputEnabled = false;
+			removeMouseListener(this);
+			removeMouseMotionListener(this);
+			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 updateFramebufferSize() {
+
+		// Useful shortcuts.
+		int fbWidth = rfb.framebufferWidth;
+		int fbHeight = rfb.framebufferHeight;
+
+		// Calculate scaling factor for auto scaling.
+		if (maxWidth > 0 && maxHeight > 0) {
+			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();
+			}
+		}
+
+		// Images with raw pixels should be re-allocated on every change
+		// of geometry or pixel format.
+		if (bytesPixel == 1) {
+
+			pixels24 = null;
+			pixels8 = new byte[fbWidth * fbHeight];
+
+			pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm8,
+					pixels8, 0, fbWidth);
+
+			zrleTilePixels24 = null;
+			zrleTilePixels8 = new byte[64 * 64];
+
+		} else {
+
+			pixels8 = null;
+			pixels24 = new int[fbWidth * fbHeight];
+
+			pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm24,
+					pixels24, 0, fbWidth);
+
+			zrleTilePixels8 = null;
+			zrleTilePixels24 = new int[64 * 64];
+
+		}
+		pixelsSource.setAnimated(true);
+		rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource);
+
+	}
+
+	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
+		//
+
+		long count = 0;
+
+		// single thread
+/*
+		try {
+			// rfb.setSoTimeout(1000);
+			Socket newCli = rfb.accept();
+			rfb.sendInitData(newCli);
+			rfb.addSock(newCli);
+		} catch (IOException e) {
+		}
+*/
+
+		
+		 Thread accept = new Thread(new acceptThread(rfb)); 
+		 accept.start();
+		
+
+		while (true) {
+
+//			System.out.println("\ncount=" + count);
+			count++;
+			
+			rfb.regiFramebufferUpdate();
+			rfb.checkAndMark();
+//			rfb.printFramebufferUpdate();
+
+			int bufSize = (int)rfb.getNumBytesRead();
+			
+			// 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:
+						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;
+				}
+*/
+				// Request framebuffer update if needed.
+				int w = rfb.framebufferWidth;
+				int h = rfb.framebufferHeight;
+				rfb.writeFramebufferUpdateRequest(0, 0, w, h, !fullUpdateNeeded);
+
+				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);
+			}
+			bufSize = (int)rfb.getNumBytesRead() - bufSize;
+//			System.out.println("bufSize="+bufSize);
+			rfb.bufResetSend(bufSize);
+		}
+	}
+
+	//
+	// 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 {
+		handleRawRect(x, y, w, h, true);
+	}
+
+	void handleRawRect(int x, int y, int w, int h, boolean paint)
+			throws IOException {
+
+		if (bytesPixel == 1) {
+			for (int dy = y; dy < y + h; dy++) {
+				rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w);
+				if (rfb.rec != null) {
+					rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w);
+				}
+			}
+		} else {
+			byte[] buf = new byte[w * 4];
+			int i, offset;
+			for (int dy = y; dy < y + h; dy++) {
+				rfb.readFully(buf);
+				if (rfb.rec != null) {
+					rfb.rec.write(buf);
+				}
+				/*
+				 * offset = dy * rfb.framebufferWidth + x; for (i = 0; i < w;
+				 * i++) { pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16 |
+				 * (buf[i * 4 + 1] & 0xFF) << 8 | (buf[i * 4] & 0xFF); }
+				 */
+			}
+		}
+		/*
+		 * handleUpdatedPixels(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 {
+
+		rfb.readCopyRect();
+		memGraphics.copyArea(rfb.copyRectSrcX, rfb.copyRectSrcY, w, h, x
+				- rfb.copyRectSrcX, y - rfb.copyRectSrcY);
+
+		scheduleRepaint(x, y, w, h);
+	}
+
+	//
+	// Handle an RRE-encoded rectangle.
+	//
+
+	void handleRRERect(int x, int y, int w, int h) throws IOException {
+
+		int nSubrects = rfb.readU32();
+
+		byte[] bg_buf = new byte[bytesPixel];
+		rfb.readFully(bg_buf);
+		Color pixel;
+		if (bytesPixel == 1) {
+			pixel = colors[bg_buf[0] & 0xFF];
+		} else {
+			pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF,
+					bg_buf[0] & 0xFF);
+		}
+		memGraphics.setColor(pixel);
+		memGraphics.fillRect(x, y, w, h);
+
+		byte[] buf = new byte[nSubrects * (bytesPixel + 8)];
+		rfb.readFully(buf);
+		DataInputStream ds = new DataInputStream(new ByteArrayInputStream(buf));
+
+		if (rfb.rec != null) {
+			rfb.rec.writeIntBE(nSubrects);
+			rfb.rec.write(bg_buf);
+			rfb.rec.write(buf);
+		}
+
+		int sx, sy, sw, sh;
+
+		for (int j = 0; j < nSubrects; j++) {
+			if (bytesPixel == 1) {
+				pixel = colors[ds.readUnsignedByte()];
+			} else {
+				ds.skip(4);
+				pixel = new Color(buf[j * 12 + 2] & 0xFF,
+						buf[j * 12 + 1] & 0xFF, buf[j * 12] & 0xFF);
+			}
+			sx = x + ds.readUnsignedShort();
+			sy = y + ds.readUnsignedShort();
+			sw = ds.readUnsignedShort();
+			sh = ds.readUnsignedShort();
+
+			memGraphics.setColor(pixel);
+			memGraphics.fillRect(sx, sy, sw, sh);
+		}
+
+		scheduleRepaint(x, y, w, h);
+	}
+
+	//
+	// Handle a CoRRE-encoded rectangle.
+	//
+
+	void handleCoRRERect(int x, int y, int w, int h) throws IOException {
+		int nSubrects = rfb.readU32();
+
+		byte[] bg_buf = new byte[bytesPixel];
+		rfb.readFully(bg_buf);
+		Color pixel;
+		if (bytesPixel == 1) {
+			pixel = colors[bg_buf[0] & 0xFF];
+		} else {
+			pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF,
+					bg_buf[0] & 0xFF);
+		}
+		memGraphics.setColor(pixel);
+		memGraphics.fillRect(x, y, w, h);
+
+		byte[] buf = new byte[nSubrects * (bytesPixel + 4)];
+		rfb.readFully(buf);
+
+		if (rfb.rec != null) {
+			rfb.rec.writeIntBE(nSubrects);
+			rfb.rec.write(bg_buf);
+			rfb.rec.write(buf);
+		}
+
+		int sx, sy, sw, sh;
+		int i = 0;
+
+		for (int j = 0; j < nSubrects; j++) {
+			if (bytesPixel == 1) {
+				pixel = colors[buf[i++] & 0xFF];
+			} else {
+				pixel = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF,
+						buf[i] & 0xFF);
+				i += 4;
+			}
+			sx = x + (buf[i++] & 0xFF);
+			sy = y + (buf[i++] & 0xFF);
+			sw = buf[i++] & 0xFF;
+			sh = buf[i++] & 0xFF;
+
+			memGraphics.setColor(pixel);
+			memGraphics.fillRect(sx, sy, sw, sh);
+		}
+
+		scheduleRepaint(x, y, w, h);
+	}
+
+	//
+	// Handle a Hextile-encoded rectangle.
+	//
+
+	// These colors should be kept between handleHextileSubrect() calls.
+	private Color hextile_bg, hextile_fg;
+
+	void handleHextileRect(int x, int y, int w, int h) throws IOException {
+
+		hextile_bg = new Color(0);
+		hextile_fg = new Color(0);
+
+		for (int ty = y; ty < y + h; ty += 16) {
+			int th = 16;
+			if (y + h - ty < 16)
+				th = y + h - ty;
+
+			for (int tx = x; tx < x + w; tx += 16) {
+				int tw = 16;
+				if (x + w - tx < 16)
+					tw = x + w - tx;
+
+				handleHextileSubrect(tx, ty, tw, th);
+			}
+
+			// Finished with a row of tiles, now let's show it.
+			scheduleRepaint(x, y, w, h);
+		}
+	}
+
+	//
+	// Handle one tile in the Hextile-encoded data.
+	//
+
+	void handleHextileSubrect(int tx, int ty, int tw, int th)
+			throws IOException {
+
+		int subencoding = rfb.readU8();
+		if (rfb.rec != null) {
+			rfb.rec.writeByte(subencoding);
+		}
+
+		// Is it a raw-encoded sub-rectangle?
+		if ((subencoding & rfb.HextileRaw) != 0) {
+			handleRawRect(tx, ty, tw, th, false);
+			return;
+		}
+
+		// Read and draw the background if specified.
+		byte[] cbuf = new byte[bytesPixel];
+		if ((subencoding & rfb.HextileBackgroundSpecified) != 0) {
+			rfb.readFully(cbuf);
+			if (bytesPixel == 1) {
+				hextile_bg = colors[cbuf[0] & 0xFF];
+			} else {
+				hextile_bg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF,
+						cbuf[0] & 0xFF);
+			}
+			if (rfb.rec != null) {
+				rfb.rec.write(cbuf);
+			}
+		}
+		memGraphics.setColor(hextile_bg);
+		memGraphics.fillRect(tx, ty, tw, th);
+
+		// Read the foreground color if specified.
+		if ((subencoding & rfb.HextileForegroundSpecified) != 0) {
+			rfb.readFully(cbuf);
+			if (bytesPixel == 1) {
+				hextile_fg = colors[cbuf[0] & 0xFF];
+			} else {
+				hextile_fg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF,
+						cbuf[0] & 0xFF);
+			}
+			if (rfb.rec != null) {
+				rfb.rec.write(cbuf);
+			}
+		}
+
+		// Done with this tile if there is no sub-rectangles.
+		if ((subencoding & rfb.HextileAnySubrects) == 0)
+			return;
+
+		int nSubrects = rfb.readU8();
+		int bufsize = nSubrects * 2;
+		if ((subencoding & rfb.HextileSubrectsColoured) != 0) {
+			bufsize += nSubrects * bytesPixel;
+		}
+		byte[] buf = new byte[bufsize];
+		rfb.readFully(buf);
+		if (rfb.rec != null) {
+			rfb.rec.writeByte(nSubrects);
+			rfb.rec.write(buf);
+		}
+
+		int b1, b2, sx, sy, sw, sh;
+		int i = 0;
+
+		if ((subencoding & rfb.HextileSubrectsColoured) == 0) {
+
+			// Sub-rectangles are all of the same color.
+			memGraphics.setColor(hextile_fg);
+			for (int j = 0; j < nSubrects; j++) {
+				b1 = buf[i++] & 0xFF;
+				b2 = buf[i++] & 0xFF;
+				sx = tx + (b1 >> 4);
+				sy = ty + (b1 & 0xf);
+				sw = (b2 >> 4) + 1;
+				sh = (b2 & 0xf) + 1;
+				memGraphics.fillRect(sx, sy, sw, sh);
+			}
+		} else if (bytesPixel == 1) {
+
+			// BGR233 (8-bit color) version for colored sub-rectangles.
+			for (int j = 0; j < nSubrects; j++) {
+				hextile_fg = colors[buf[i++] & 0xFF];
+				b1 = buf[i++] & 0xFF;
+				b2 = buf[i++] & 0xFF;
+				sx = tx + (b1 >> 4);
+				sy = ty + (b1 & 0xf);
+				sw = (b2 >> 4) + 1;
+				sh = (b2 & 0xf) + 1;
+				memGraphics.setColor(hextile_fg);
+				memGraphics.fillRect(sx, sy, sw, sh);
+			}
+
+		} else {
+
+			// Full-color (24-bit) version for colored sub-rectangles.
+			for (int j = 0; j < nSubrects; j++) {
+				hextile_fg = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF,
+						buf[i] & 0xFF);
+				i += 4;
+				b1 = buf[i++] & 0xFF;
+				b2 = buf[i++] & 0xFF;
+				sx = tx + (b1 >> 4);
+				sy = ty + (b1 & 0xf);
+				sw = (b2 >> 4) + 1;
+				sh = (b2 & 0xf) + 1;
+				memGraphics.setColor(hextile_fg);
+				memGraphics.fillRect(sx, sy, sw, sh);
+			}
+
+		}
+	}
+
+	//
+	// 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 {
+
+		if (zrleInStream == null)
+			zrleInStream = new ZlibInStream();
+
+		int nBytes = rfb.readU32();
+		if (nBytes > 64 * 1024 * 1024)
+			throw new Exception("ZRLE decoder: illegal compressed data size");
+
+		if (zrleBuf == null || zrleBufLen < nBytes) {
+			zrleBufLen = nBytes + 4096;
+			zrleBuf = new byte[zrleBufLen];
+		}
+
+		// FIXME: Do not wait for all the data before decompression.
+		rfb.readFully(zrleBuf, 0, nBytes);
+
+		if (rfb.rec != null) {
+			if (rfb.recordFromBeginning) {
+				rfb.rec.writeIntBE(nBytes);
+				rfb.rec.write(zrleBuf, 0, nBytes);
+			} else if (!zrleRecWarningShown) {
+				System.out.println("Warning: ZRLE session can be recorded"
+						+ " only from the beginning");
+				System.out.println("Warning: Recorded file may be corrupted");
+				zrleRecWarningShown = true;
+			}
+
+		}
+
+		zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes);
+
+		for (int ty = y; ty < y + h; ty += 64) {
+
+			int th = Math.min(y + h - ty, 64);
+
+			for (int tx = x; tx < x + w; tx += 64) {
+
+				int tw = Math.min(x + w - tx, 64);
+
+				int mode = zrleInStream.readU8();
+				boolean rle = (mode & 128) != 0;
+				int palSize = mode & 127;
+				int[] palette = new int[128];
+
+				readZrlePalette(palette, palSize);
+
+				if (palSize == 1) {
+					int pix = palette[0];
+					Color c = (bytesPixel == 1) ? colors[pix] : new Color(
+							0xFF000000 | pix);
+					memGraphics.setColor(c);
+					memGraphics.fillRect(tx, ty, tw, th);
+					continue;
+				}
+
+				if (!rle) {
+					if (palSize == 0) {
+						readZrleRawPixels(tw, th);
+					} else {
+						readZrlePackedPixels(tw, th, palette, palSize);
+					}
+				} else {
+					if (palSize == 0) {
+						readZrlePlainRLEPixels(tw, th);
+					} else {
+						readZrlePackedRLEPixels(tw, th, palette);
+					}
+				}
+				handleUpdatedZrleTile(tx, ty, tw, th);
+			}
+		}
+
+		zrleInStream.reset();
+
+		scheduleRepaint(x, y, w, h);
+	}
+
+	int readPixel(InStream is) throws Exception {
+		int pix;
+
+		if (bytesPixel == 1) {
+
+			pix = is.readU8();
+		} else {
+			int p1 = is.readU8();
+			int p2 = is.readU8();
+			int p3 = is.readU8();
+			pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF);
+		}
+		return pix;
+	}
+
+	void readPixels(InStream is, int[] dst, int count) throws Exception {
+		int pix;
+		if (bytesPixel == 1) {
+			byte[] buf = new byte[count];
+			is.readBytes(buf, 0, count);
+			for (int i = 0; i < count; i++) {
+				dst[i] = (int) buf[i] & 0xFF;
+			}
+		} else {
+			byte[] buf = new byte[count * 3];
+			is.readBytes(buf, 0, count * 3);
+			for (int i = 0; i < count; i++) {
+				dst[i] = ((buf[i * 3 + 2] & 0xFF) << 16
+						| (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3] & 0xFF));
+			}
+		}
+	}
+
+	void readZrlePalette(int[] palette, int palSize) throws Exception {
+		readPixels(zrleInStream, palette, palSize);
+	}
+
+	void readZrleRawPixels(int tw, int th) throws Exception {
+		if (bytesPixel == 1) {
+			zrleInStream.readBytes(zrleTilePixels8, 0, tw * th);
+		} else {
+			readPixels(zrleInStream, zrleTilePixels24, tw * th); // /
+		}
+	}
+
+	void readZrlePackedPixels(int tw, int th, int[] palette, int palSize)
+			throws Exception {
+
+		int bppp = ((palSize > 16) ? 8 : ((palSize > 4) ? 4
+				: ((palSize > 2) ? 2 : 1)));
+		int ptr = 0;
+
+		for (int i = 0; i < th; i++) {
+			int eol = ptr + tw;
+			int b = 0;
+			int nbits = 0;
+
+			while (ptr < eol) {
+				if (nbits == 0) {
+					b = zrleInStream.readU8();
+					nbits = 8;
+				}
+				nbits -= bppp;
+				int index = (b >> nbits) & ((1 << bppp) - 1) & 127;
+				if (bytesPixel == 1) {
+					zrleTilePixels8[ptr++] = (byte) palette[index];
+				} else {
+					zrleTilePixels24[ptr++] = palette[index];
+				}
+			}
+		}
+	}
+
+	void readZrlePlainRLEPixels(int tw, int th) throws Exception {
+		int ptr = 0;
+		int end = ptr + tw * th;
+		while (ptr < end) {
+			int pix = readPixel(zrleInStream);
+			int len = 1;
+			int b;
+			do {
+				b = zrleInStream.readU8();
+				len += b;
+			} while (b == 255);
+
+			if (!(len <= end - ptr))
+				throw new Exception("ZRLE decoder: assertion failed"
+						+ " (len <= end-ptr)");
+
+			if (bytesPixel == 1) {
+				while (len-- > 0)
+					zrleTilePixels8[ptr++] = (byte) pix;
+			} else {
+				while (len-- > 0)
+					zrleTilePixels24[ptr++] = pix;
+			}
+		}
+	}
+
+	void readZrlePackedRLEPixels(int tw, int th, int[] palette)
+			throws Exception {
+
+		int ptr = 0;
+		int end = ptr + tw * th;
+		while (ptr < end) {
+			int index = zrleInStream.readU8();
+			int len = 1;
+			if ((index & 128) != 0) {
+				int b;
+				do {
+					b = zrleInStream.readU8();
+					len += b;
+				} while (b == 255);
+
+				if (!(len <= end - ptr))
+					throw new Exception("ZRLE decoder: assertion failed"
+							+ " (len <= end - ptr)");
+			}
+
+			index &= 127;
+			int pix = palette[index];
+
+			if (bytesPixel == 1) {
+				while (len-- > 0)
+					zrleTilePixels8[ptr++] = (byte) pix;
+			} else {
+				while (len-- > 0)
+					zrleTilePixels24[ptr++] = pix;
+			}
+		}
+	}
+
+	//
+	// Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update.
+	//
+
+	void handleUpdatedZrleTile(int x, int y, int w, int h) {
+		Object src, dst;
+		if (bytesPixel == 1) {
+			src = zrleTilePixels8;
+			dst = pixels8;
+		} else {
+			src = zrleTilePixels24;
+			dst = pixels24;
+		}
+		int offsetSrc = 0;
+		int offsetDst = (y * rfb.framebufferWidth + x);
+		for (int j = 0; j < h; j++) {
+			System.arraycopy(src, offsetSrc, dst, offsetDst, w);
+			offsetSrc += w;
+			offsetDst += rfb.framebufferWidth;
+		}
+		handleUpdatedPixels(x, y, w, h);
+	}
+
+	//
+	// Handle a Zlib-encoded rectangle.
+	//
+
+	void handleZlibRect(int x, int y, int w, int h) throws Exception {
+
+		int nBytes = rfb.readU32();
+
+		if (zlibBuf == null || zlibBufLen < nBytes) {
+			zlibBufLen = nBytes * 2;
+			zlibBuf = new byte[zlibBufLen];
+		}
+
+		rfb.readFully(zlibBuf, 0, nBytes);
+
+		if (rfb.rec != null && rfb.recordFromBeginning) {
+			rfb.rec.writeIntBE(nBytes);
+			rfb.rec.write(zlibBuf, 0, nBytes);
+		}
+
+		if (zlibInflater == null) {
+			zlibInflater = new Inflater();
+		}
+		zlibInflater.setInput(zlibBuf, 0, nBytes);
+
+		if (bytesPixel == 1) {
+			for (int dy = y; dy < y + h; dy++) {
+				zlibInflater.inflate(pixels8, dy * rfb.framebufferWidth + x, w);
+				if (rfb.rec != null && !rfb.recordFromBeginning)
+					rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w);
+			}
+		} else {
+			byte[] buf = new byte[w * 4];
+			int i, offset;
+			for (int dy = y; dy < y + h; dy++) {
+				zlibInflater.inflate(buf);
+				offset = dy * rfb.framebufferWidth + x;
+				for (i = 0; i < w; i++) {
+					pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16
+							| (buf[i * 4 + 1] & 0xFF) << 8
+							| (buf[i * 4] & 0xFF);
+				}
+				if (rfb.rec != null && !rfb.recordFromBeginning)
+					rfb.rec.write(buf);
+			}
+		}
+
+		handleUpdatedPixels(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 {
+
+		int comp_ctl = rfb.readU8();
+		if (rfb.rec != null) {
+			if (rfb.recordFromBeginning || comp_ctl == (rfb.TightFill << 4)
+					|| comp_ctl == (rfb.TightJpeg << 4)) {
+				// Send data exactly as received.
+				rfb.rec.writeByte(comp_ctl);
+			} else {
+				// Tell the decoder to flush each of the four zlib streams.
+				rfb.rec.writeByte(comp_ctl | 0x0F);
+			}
+		}
+
+		// Flush zlib streams if we are told by the server to do so.
+		for (int stream_id = 0; stream_id < 4; stream_id++) {
+			if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) {
+				tightInflaters[stream_id] = null;
+			}
+			comp_ctl >>= 1;
+		}
+
+		// Check correctness of subencoding value.
+		if (comp_ctl > rfb.TightMaxSubencoding) {
+			throw new Exception("Incorrect tight subencoding: " + comp_ctl);
+		}
+
+		// Handle solid-color rectangles.
+		if (comp_ctl == rfb.TightFill) {
+
+			if (bytesPixel == 1) {
+				int idx = rfb.readU8();
+				memGraphics.setColor(colors[idx]);
+				if (rfb.rec != null) {
+					rfb.rec.writeByte(idx);
+				}
+			} else {
+				byte[] buf = new byte[3];
+				rfb.readFully(buf);
+				if (rfb.rec != null) {
+					rfb.rec.write(buf);
+				}
+				Color bg = new Color(0xFF000000 | (buf[0] & 0xFF) << 16
+						| (buf[1] & 0xFF) << 8 | (buf[2] & 0xFF));
+				memGraphics.setColor(bg);
+			}
+			memGraphics.fillRect(x, y, w, h);
+			scheduleRepaint(x, y, w, h);
+			return;
+
+		}
+
+		if (comp_ctl == rfb.TightJpeg) {
+
+			statNumRectsTightJPEG++;
+
+			// Read JPEG data.
+			byte[] jpegData = new byte[rfb.readCompactLen()];
+			rfb.readFully(jpegData);
+			if (rfb.rec != null) {
+				if (!rfb.recordFromBeginning) {
+					rfb.recordCompactLen(jpegData.length);
+				}
+				rfb.rec.write(jpegData);
+			}
+
+			// Create an Image object from the JPEG data.
+			Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData);
+
+			// Remember the rectangle where the image should be drawn.
+			jpegRect = new Rectangle(x, y, w, h);
+
+			// Let the imageUpdate() method do the actual drawing, here just
+			// wait until the image is fully loaded and drawn.
+			synchronized (jpegRect) {
+				Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1,
+						this);
+				try {
+					// Wait no longer than three seconds.
+					jpegRect.wait(3000);
+				} catch (InterruptedException e) {
+					throw new Exception("Interrupted while decoding JPEG image");
+				}
+			}
+
+			// Done, jpegRect is not needed any more.
+			jpegRect = null;
+			return;
+
+		}
+
+		// Read filter id and parameters.
+		int numColors = 0, rowSize = w;
+		byte[] palette8 = new byte[2];
+		int[] palette24 = new int[256];
+		boolean useGradient = false;
+		if ((comp_ctl & rfb.TightExplicitFilter) != 0) {
+			int filter_id = rfb.readU8();
+			if (rfb.rec != null) {
+				rfb.rec.writeByte(filter_id);
+			}
+			if (filter_id == rfb.TightFilterPalette) {
+				numColors = rfb.readU8() + 1;
+				if (rfb.rec != null) {
+					rfb.rec.writeByte(numColors - 1);
+				}
+				if (bytesPixel == 1) {
+					if (numColors != 2) {
+						throw new Exception("Incorrect tight palette size: "
+								+ numColors);
+					}
+					rfb.readFully(palette8);
+					if (rfb.rec != null) {
+						rfb.rec.write(palette8);
+					}
+				} else {
+					byte[] buf = new byte[numColors * 3];
+					rfb.readFully(buf);
+					if (rfb.rec != null) {
+						rfb.rec.write(buf);
+					}
+					for (int i = 0; i < numColors; i++) {
+						palette24[i] = ((buf[i * 3] & 0xFF) << 16
+								| (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3 + 2] & 0xFF));
+					}
+				}
+				if (numColors == 2)
+					rowSize = (w + 7) / 8;
+			} else if (filter_id == rfb.TightFilterGradient) {
+				useGradient = true;
+			} else if (filter_id != rfb.TightFilterCopy) {
+				throw new Exception("Incorrect tight filter id: " + filter_id);
+			}
+		}
+		if (numColors == 0 && bytesPixel == 4)
+			rowSize *= 3;
+
+		// Read, optionally uncompress and decode data.
+		int dataSize = h * rowSize;
+		if (dataSize < rfb.TightMinToCompress) {
+			// Data size is small - not compressed with zlib.
+			if (numColors != 0) {
+				// Indexed colors.
+				byte[] indexedData = new byte[dataSize];
+				rfb.readFully(indexedData);
+				if (rfb.rec != null) {
+					rfb.rec.write(indexedData);
+				}
+				if (numColors == 2) {
+					// Two colors.
+					if (bytesPixel == 1) {
+						decodeMonoData(x, y, w, h, indexedData, palette8);
+					} else {
+						decodeMonoData(x, y, w, h, indexedData, palette24);
+					}
+				} else {
+					// 3..255 colors (assuming bytesPixel == 4).
+					int i = 0;
+					for (int dy = y; dy < y + h; dy++) {
+						for (int dx = x; dx < x + w; dx++) {
+							pixels24[dy * rfb.framebufferWidth + dx] = palette24[indexedData[i++] & 0xFF];
+						}
+					}
+				}
+			} else if (useGradient) {
+				// "Gradient"-processed data
+				byte[] buf = new byte[w * h * 3];
+				rfb.readFully(buf);
+				if (rfb.rec != null) {
+					rfb.rec.write(buf);
+				}
+				decodeGradientData(x, y, w, h, buf);
+			} else {
+				// Raw truecolor data.
+				if (bytesPixel == 1) {
+					for (int dy = y; dy < y + h; dy++) {
+						rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w);
+						if (rfb.rec != null) {
+							rfb.rec.write(pixels8, dy * rfb.framebufferWidth
+									+ x, w);
+						}
+					}
+				} else {
+					byte[] buf = new byte[w * 3];
+					int i, offset;
+					for (int dy = y; dy < y + h; dy++) {
+						rfb.readFully(buf);
+						if (rfb.rec != null) {
+							rfb.rec.write(buf);
+						}
+						offset = dy * rfb.framebufferWidth + x;
+						for (i = 0; i < w; i++) {
+							pixels24[offset + i] = (buf[i * 3] & 0xFF) << 16
+									| (buf[i * 3 + 1] & 0xFF) << 8
+									| (buf[i * 3 + 2] & 0xFF);
+						}
+					}
+				}
+			}
+		} else {
+			// Data was compressed with zlib.
+			int zlibDataLen = rfb.readCompactLen();
+			byte[] zlibData = new byte[zlibDataLen];
+			rfb.readFully(zlibData);
+			if (rfb.rec != null && rfb.recordFromBeginning) {
+				rfb.rec.write(zlibData);
+			}
+			int stream_id = comp_ctl & 0x03;
+			if (tightInflaters[stream_id] == null) {
+				tightInflaters[stream_id] = new Inflater();
+			}
+			Inflater myInflater = tightInflaters[stream_id];
+			myInflater.setInput(zlibData);
+			byte[] buf = new byte[dataSize];
+			myInflater.inflate(buf);
+			if (rfb.rec != null && !rfb.recordFromBeginning) {
+				rfb.recordCompressedData(buf);
+			}
+
+			if (numColors != 0) {
+				// Indexed colors.
+				if (numColors == 2) {
+					// Two colors.
+					if (bytesPixel == 1) {
+						decodeMonoData(x, y, w, h, buf, palette8);
+					} else {
+						decodeMonoData(x, y, w, h, buf, palette24);
+					}
+				} else {
+					// More than two colors (assuming bytesPixel == 4).
+					int i = 0;
+					for (int dy = y; dy < y + h; dy++) {
+						for (int dx = x; dx < x + w; dx++) {
+							pixels24[dy * rfb.framebufferWidth + dx] = palette24[buf[i++] & 0xFF];
+						}
+					}
+				}
+			} else if (useGradient) {
+				// Compressed "Gradient"-filtered data (assuming bytesPixel ==
+				// 4).
+				decodeGradientData(x, y, w, h, buf);
+			} else {
+				// Compressed truecolor data.
+				if (bytesPixel == 1) {
+					int destOffset = y * rfb.framebufferWidth + x;
+					for (int dy = 0; dy < h; dy++) {
+						System.arraycopy(buf, dy * w, pixels8, destOffset, w);
+						destOffset += rfb.framebufferWidth;
+					}
+				} else {
+					int srcOffset = 0;
+					int destOffset, i;
+					for (int dy = 0; dy < h; dy++) {
+						myInflater.inflate(buf);
+						destOffset = (y + dy) * rfb.framebufferWidth + x;
+						for (i = 0; i < w; i++) {
+							pixels24[destOffset + i] = (buf[srcOffset] & 0xFF) << 16
+									| (buf[srcOffset + 1] & 0xFF) << 8
+									| (buf[srcOffset + 2] & 0xFF);
+							srcOffset += 3;
+						}
+					}
+				}
+			}
+		}
+
+		handleUpdatedPixels(x, y, w, h);
+		scheduleRepaint(x, y, w, h);
+	}
+
+	//
+	// Decode 1bpp-encoded bi-color rectangle (8-bit and 24-bit versions).
+	//
+
+	void decodeMonoData(int x, int y, int w, int h, byte[] src, byte[] palette) {
+
+		int dx, dy, n;
+		int i = y * rfb.framebufferWidth + x;
+		int rowBytes = (w + 7) / 8;
+		byte b;
+
+		for (dy = 0; dy < h; dy++) {
+			for (dx = 0; dx < w / 8; dx++) {
+				b = src[dy * rowBytes + dx];
+				for (n = 7; n >= 0; n--)
+					pixels8[i++] = palette[b >> n & 1];
+			}
+			for (n = 7; n >= 8 - w % 8; n--) {
+				pixels8[i++] = palette[src[dy * rowBytes + dx] >> n & 1];
+			}
+			i += (rfb.framebufferWidth - w);
+		}
+	}
+
+	void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette) {
+
+		int dx, dy, n;
+		int i = y * rfb.framebufferWidth + x;
+		int rowBytes = (w + 7) / 8;
+		byte b;
+
+		for (dy = 0; dy < h; dy++) {
+			for (dx = 0; dx < w / 8; dx++) {
+				b = src[dy * rowBytes + dx];
+				for (n = 7; n >= 0; n--)
+					pixels24[i++] = palette[b >> n & 1];
+			}
+			for (n = 7; n >= 8 - w % 8; n--) {
+				pixels24[i++] = palette[src[dy * rowBytes + dx] >> n & 1];
+			}
+			i += (rfb.framebufferWidth - w);
+		}
+	}
+
+	//
+	// Decode data processed with the "Gradient" filter.
+	//
+
+	void decodeGradientData(int x, int y, int w, int h, byte[] buf) {
+
+		int dx, dy, c;
+		byte[] prevRow = new byte[w * 3];
+		byte[] thisRow = new byte[w * 3];
+		byte[] pix = new byte[3];
+		int[] est = new int[3];
+
+		int offset = y * rfb.framebufferWidth + x;
+
+		for (dy = 0; dy < h; dy++) {
+
+			/* First pixel in a row */
+			for (c = 0; c < 3; c++) {
+				pix[c] = (byte) (prevRow[c] + buf[dy * w * 3 + c]);
+				thisRow[c] = pix[c];
+			}
+			pixels24[offset++] = (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8
+					| (pix[2] & 0xFF);
+
+			/* Remaining pixels of a row */
+			for (dx = 1; dx < w; dx++) {
+				for (c = 0; c < 3; c++) {
+					est[c] = ((prevRow[dx * 3 + c] & 0xFF) + (pix[c] & 0xFF) - (prevRow[(dx - 1)
+							* 3 + c] & 0xFF));
+					if (est[c] > 0xFF) {
+						est[c] = 0xFF;
+					} else if (est[c] < 0x00) {
+						est[c] = 0x00;
+					}
+					pix[c] = (byte) (est[c] + buf[(dy * w + dx) * 3 + c]);
+					thisRow[dx * 3 + c] = pix[c];
+				}
+				pixels24[offset++] = (pix[0] & 0xFF) << 16
+						| (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
+			}
+
+			System.arraycopy(thisRow, 0, prevRow, 0, w * 3);
+			offset += (rfb.framebufferWidth - w);
+		}
+	}
+
+	//
+	// Display newly updated area of pixels.
+	//
+
+	void handleUpdatedPixels(int x, int y, int w, int h) {
+
+		// Draw updated pixels of the off-screen image.
+		pixelsSource.newPixels(x, y, w, h);
+		memGraphics.setClip(x, y, w, h);
+		memGraphics.drawImage(rawPixelsImage, 0, 0, null);
+		memGraphics.setClip(0, 0, rfb.framebufferWidth, rfb.framebufferHeight);
+	}
+
+	//
+	// Tell JVM to repaint specified desktop area.
+	//
+
+	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);
+	}
+
+	public 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();
+	}
+
+	public void processLocalMouseEvent(MouseEvent evt, boolean moved) {
+		if (viewer.rfb != null && rfb.inNormalProtocol) {
+			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();
+			}
+		}
+	}
+
+	//
+	// Ignored events.
+	//
+
+	public void mouseClicked(MouseEvent evt) {
+	}
+
+	public void mouseEntered(MouseEvent evt) {
+	}
+
+	public void mouseExited(MouseEvent evt) {
+	}
+
+	//
+	// 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;
+	}
+
+	// ////////////////////////////////////////////////////////////////
+	//
+	// Handle cursor shape updates (XCursor and RichCursor encodings).
+	//
+
+	boolean showSoftCursor = false;
+
+	MemoryImageSource softCursorSource;
+	Image softCursor;
+
+	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 * bytesPixel + 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 pixByte, 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);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/RecordingFrame.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,312 @@
+package myVncProxy;
+//
+//  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.
+//
+
+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("TightVNC 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);
+
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/ReloginPanel.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,66 @@
+package myVncProxy;
+//
+//  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
+//
+
+
+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());
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/RfbProto.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,1380 @@
+package myVncProxy;
+//
+//  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
+//
+
+import java.io.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.net.Socket;
+import java.net.ServerSocket;
+import java.util.zip.*;
+import java.nio.*;
+
+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";
+
+	// Vendor signatures: standard VNC/RealVNC, TridiaVNC, and TightVNC
+	final static String StandardVendor = "STDV", TridiaVncVendor = "TRDV",
+			TightVncVendor = "TGHT";
+
+	// Security types
+	final static int SecTypeInvalid = 0, SecTypeNone = 1, SecTypeVncAuth = 2,
+			SecTypeTight = 16;
+
+	// Supported tunneling types
+	final static int NoTunneling = 0;
+	final static String SigNoTunneling = "NOTUNNEL";
+
+	// Supported authentication types
+	final static int AuthNone = 1, AuthVNC = 2, AuthUnixLogin = 129;
+	final static String SigAuthNone = "NOAUTH__", SigAuthVNC = "VNCAUTH_",
+			SigAuthUnixLogin = "ULGNAUTH";
+
+	// 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;
+
+	// Non-standard server-to-client messages
+	final static int EndOfContinuousUpdates = 150;
+	final static String SigEndOfContinuousUpdates = "CUS_EOCU";
+
+	// Standard client-to-server messages
+	final static int SetPixelFormat = 0, FixColourMapEntries = 1,
+			SetEncodings = 2, FramebufferUpdateRequest = 3, KeyboardEvent = 4,
+			PointerEvent = 5, ClientCutText = 6;
+
+	// Non-standard client-to-server messages
+	final static int EnableContinuousUpdates = 150;
+	final static String SigEnableContinuousUpdates = "CUC_ENCU";
+
+	// 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 String SigEncodingRaw = "RAW_____",
+			SigEncodingCopyRect = "COPYRECT", SigEncodingRRE = "RRE_____",
+			SigEncodingCoRRE = "CORRE___", SigEncodingHextile = "HEXTILE_",
+			SigEncodingZlib = "ZLIB____", SigEncodingTight = "TIGHT___",
+			SigEncodingZRLE = "ZRLE____",
+			SigEncodingCompressLevel0 = "COMPRLVL",
+			SigEncodingQualityLevel0 = "JPEGQLVL",
+			SigEncodingXCursor = "X11CURSR",
+			SigEncodingRichCursor = "RCHCURSR",
+			SigEncodingPointerPos = "POINTPOS",
+			SigEncodingLastRect = "LASTRECT",
+			SigEncodingNewFBSize = "NEWFBSIZ";
+
+	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;
+	protected DataInputStream is;
+//	private long numBytesRead = 0;
+	protected 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 will be set to false if the startSession() was called after
+	// we have received at least one Zlib-, ZRLE- or Tight-encoded
+	// framebuffer update.
+	boolean recordFromBeginning = true;
+
+	// 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;
+	boolean protocolTightVNC;
+	CapsContainer tunnelCaps, authCaps;
+	CapsContainer serverMsgCaps, clientMsgCaps;
+	CapsContainer encodingCaps;
+
+	// If true, informs that the RFB socket was closed.
+//	private boolean closed;
+	protected 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);
+		} 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;
+	}
+	
+	RfbProto(String h, int p) throws IOException {
+		host = h;
+		port = p;
+
+		sock = new Socket(host, port);
+		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());
+		}
+		protocolTightVNC = false;
+		initCapabilities();
+	}
+
+	//
+	// 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 out if the server supports TightVNC protocol extensions
+		for (int i = 0; i < nSecTypes; i++) {
+			if (secTypes[i] == SecTypeTight) {
+				protocolTightVNC = true;
+				os.write(SecTypeTight);
+				return SecTypeTight;
+			}
+		}
+
+		// Find first supported security type.
+		for (int i = 0; i < nSecTypes; i++) {
+			if (secTypes[i] == SecTypeNone || secTypes[i] == SecTypeVncAuth) {
+				secType = secTypes[i];
+				break;
+			}
+		}
+
+		if (secType == SecTypeInvalid) {
+			throw new Exception("Server did not offer supported security type");
+		} else {
+			os.write(secType);
+		}
+
+		return secType;
+	}
+
+	//
+	// 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");
+	}
+
+	//
+	// 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));
+	}
+
+	//
+	// Initialize capability lists (TightVNC protocol extensions).
+	//
+
+	void initCapabilities() {
+		tunnelCaps = new CapsContainer();
+		authCaps = new CapsContainer();
+		serverMsgCaps = new CapsContainer();
+		clientMsgCaps = new CapsContainer();
+		encodingCaps = new CapsContainer();
+
+		// Supported authentication methods
+		authCaps.add(AuthNone, StandardVendor, SigAuthNone, "No authentication");
+		authCaps.add(AuthVNC, StandardVendor, SigAuthVNC,
+				"Standard VNC password authentication");
+
+		// Supported non-standard server-to-client messages
+		// [NONE]
+
+		// Supported non-standard client-to-server messages
+		// [NONE]
+
+		// Supported encoding types
+		encodingCaps.add(EncodingCopyRect, StandardVendor, SigEncodingCopyRect,
+				"Standard CopyRect encoding");
+		encodingCaps.add(EncodingRRE, StandardVendor, SigEncodingRRE,
+				"Standard RRE encoding");
+		encodingCaps.add(EncodingCoRRE, StandardVendor, SigEncodingCoRRE,
+				"Standard CoRRE encoding");
+		encodingCaps.add(EncodingHextile, StandardVendor, SigEncodingHextile,
+				"Standard Hextile encoding");
+		encodingCaps.add(EncodingZRLE, StandardVendor, SigEncodingZRLE,
+				"Standard ZRLE encoding");
+		encodingCaps.add(EncodingZlib, TridiaVncVendor, SigEncodingZlib,
+				"Zlib encoding");
+		encodingCaps.add(EncodingTight, TightVncVendor, SigEncodingTight,
+				"Tight encoding");
+
+		// Supported pseudo-encoding types
+		encodingCaps.add(EncodingCompressLevel0, TightVncVendor,
+				SigEncodingCompressLevel0, "Compression level");
+		encodingCaps.add(EncodingQualityLevel0, TightVncVendor,
+				SigEncodingQualityLevel0, "JPEG quality level");
+		encodingCaps.add(EncodingXCursor, TightVncVendor, SigEncodingXCursor,
+				"X-style cursor shape update");
+		encodingCaps.add(EncodingRichCursor, TightVncVendor,
+				SigEncodingRichCursor, "Rich-color cursor shape update");
+		encodingCaps.add(EncodingPointerPos, TightVncVendor,
+				SigEncodingPointerPos, "Pointer position update");
+		encodingCaps.add(EncodingLastRect, TightVncVendor, SigEncodingLastRect,
+				"LastRect protocol extension");
+		encodingCaps.add(EncodingNewFBSize, TightVncVendor,
+				SigEncodingNewFBSize, "Framebuffer size change");
+	}
+
+	//
+	// Setup tunneling (TightVNC protocol extensions)
+	//
+
+	void setupTunneling() throws IOException {
+		int nTunnelTypes = readU32();
+		if (nTunnelTypes != 0) {
+			readCapabilityList(tunnelCaps, nTunnelTypes);
+
+			// We don't support tunneling yet.
+			writeInt(NoTunneling);
+		}
+	}
+
+	//
+	// Negotiate authentication scheme (TightVNC protocol extensions)
+	//
+
+	int negotiateAuthenticationTight() throws Exception {
+		int nAuthTypes = readU32();
+		if (nAuthTypes == 0)
+			return AuthNone;
+
+		readCapabilityList(authCaps, nAuthTypes);
+		for (int i = 0; i < authCaps.numEnabled(); i++) {
+			int authType = authCaps.getByOrder(i);
+			if (authType == AuthNone || authType == AuthVNC) {
+				writeInt(authType);
+				return authType;
+			}
+		}
+		throw new Exception("No suitable authentication scheme found");
+	}
+
+	//
+	// Read a capability list (TightVNC protocol extensions)
+	//
+
+	void readCapabilityList(CapsContainer caps, int count) throws IOException {
+		int code;
+		byte[] vendor = new byte[4];
+		byte[] name = new byte[8];
+		for (int i = 0; i < count; i++) {
+			code = readU32();
+			readFully(vendor);
+			readFully(name);
+			caps.enable(new CapabilityInfo(code, vendor, name));
+		}
+	}
+
+	//
+	// 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);
+*/
+			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);
+
+		// Read interaction capabilities (TightVNC protocol extensions)
+		if (protocolTightVNC) {
+			int nServerMessageTypes = readU16();
+			int nClientMessageTypes = readU16();
+			int nEncodingTypes = readU16();
+			readU16();
+			readCapabilityList(serverMsgCaps, nServerMessageTypes);
+			readCapabilityList(clientMsgCaps, nClientMessageTypes);
+			readCapabilityList(encodingCaps, nEncodingTypes);
+		}
+
+		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.
+		//
+		if (wereZlibUpdates)
+			recordFromBeginning = false;
+
+		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();
+		// System.out.println(updateNRects);
+
+		// If the session is being recorded:
+		if (rec != null) {
+			rec.writeByte(FramebufferUpdate);
+			rec.writeByte(0);
+			rec.writeShortBE(updateNRects);
+		}
+
+		numUpdatesInSession++;
+	}
+
+	// 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();
+		// System.out.println("readU16&32");
+
+		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 (updateRectEncoding == EncodingZlib && !recordFromBeginning) {
+				// Here we cannot write Zlib-encoded rectangles because the
+				// decoder won't be able to reproduce zlib stream state.
+				if (!zlibWarningShown) {
+					System.out.println("Warning: Raw encoding will be used "
+							+ "instead of Zlib in recorded session.");
+					zlibWarningShown = true;
+				}
+				rec.writeIntBE(EncodingRaw);
+			} else {
+				rec.writeIntBE(updateRectEncoding);
+				if (updateRectEncoding == EncodingTight && !recordFromBeginning
+						&& !tightWarningShown) {
+					System.out.println("Warning: Re-compressing Tight-encoded "
+							+ "updates for session recording.");
+					tightWarningShown = true;
+				}
+			}
+		}
+
+		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 CopyRect source X and Y.
+
+	int copyRectSrcX, copyRectSrcY;
+
+	void readCopyRect() throws IOException {
+		copyRectSrcX = readU16();
+		copyRectSrcY = readU16();
+
+		// If the session is being recorded:
+		if (rec != null) {
+			rec.writeShortBE(copyRectSrcX);
+			rec.writeShortBE(copyRectSrcY);
+		}
+	}
+
+	//
+	// 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;
+			}
+		}
+
+		if (rec != null && recordFromBeginning)
+			for (int i = 0; i < byteCount; i++)
+				rec.writeByte(portion[i]);
+
+		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;
+	}
+
+	//
+	// Compress and write the data into the recorded session file. This
+	// method assumes the recording is on (rec != null).
+	//
+
+	void recordCompressedData(byte[] data, int off, int len) throws IOException {
+		Deflater deflater = new Deflater();
+		deflater.setInput(data, off, len);
+		int bufSize = len + len / 100 + 12;
+		byte[] buf = new byte[bufSize];
+		deflater.finish();
+		int compressedSize = deflater.deflate(buf);
+		recordCompactLen(compressedSize);
+		rec.write(buf, 0, compressedSize);
+	}
+
+	void recordCompressedData(byte[] data) throws IOException {
+		recordCompressedData(data, 0, data.length);
+	}
+
+	//
+	// Write an integer in compact representation (1..3 bytes) into the
+	// recorded session file. This method assumes the recording is on
+	// (rec != null).
+	//
+
+	void recordCompactLen(int len) throws IOException {
+		byte[] buf = new byte[3];
+		int bytes = 0;
+		buf[bytes++] = (byte) (len & 0x7F);
+		if (len > 0x7F) {
+			buf[bytes - 1] |= 0x80;
+			buf[bytes++] = (byte) (len >> 7 & 0x7F);
+			if (len > 0x3FFF) {
+				buf[bytes - 1] |= 0x80;
+				buf[bytes++] = (byte) (len >> 14 & 0xFF);
+			}
+		}
+		rec.write(buf, 0, bytes);
+	}
+
+	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;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/SessionRecorder.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,194 @@
+package myVncProxy;
+//
+//  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.
+//
+
+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);
+  }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/SocketFactory.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,37 @@
+package myVncProxy;
+//
+//  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.
+//
+
+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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/VncCanvas.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,1920 @@
+package myVncProxy;
+//
+//  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.
+//
+
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.*;
+import java.io.*;
+import java.lang.*;
+import java.nio.ByteBuffer;
+import java.util.zip.*;
+
+import java.net.Socket;
+
+//
+// VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
+//
+
+class VncCanvas extends Canvas implements KeyListener, MouseListener,
+		MouseMotionListener {
+
+	VncViewer viewer;
+	MyRfbProto rfb;
+	ColorModel cm8, cm24;
+	Color[] colors;
+	int bytesPixel;
+
+	int maxWidth = 0, maxHeight = 0;
+	int scalingFactor;
+	int scaledWidth, scaledHeight;
+
+	Image memImage;
+	Graphics memGraphics;
+
+	Image rawPixelsImage;
+	MemoryImageSource pixelsSource;
+	byte[] pixels8;
+	int[] pixels24;
+
+	// Update statistics.
+	long statStartTime; // time on first framebufferUpdateRequest
+	int statNumUpdates; // counter for FramebufferUpdate messages
+	int statNumTotalRects; // rectangles in FramebufferUpdate messages
+	int statNumPixelRects; // the same, but excluding pseudo-rectangles
+	int statNumRectsTight; // Tight-encoded rectangles (including JPEG)
+	int statNumRectsTightJPEG; // JPEG-compressed Tight-encoded rectangles
+	int statNumRectsZRLE; // ZRLE-encoded rectangles
+	int statNumRectsHextile; // Hextile-encoded rectangles
+	int statNumRectsRaw; // Raw-encoded rectangles
+	int statNumRectsCopy; // CopyRect rectangles
+	int statNumBytesEncoded; // number of bytes in updates, as received
+	int statNumBytesDecoded; // number of bytes, as if Raw encoding was used
+
+	// ZRLE encoder's data.
+	byte[] zrleBuf;
+	int zrleBufLen = 0;
+	byte[] zrleTilePixels8;
+	int[] zrleTilePixels24;
+	ZlibInStream zrleInStream;
+	boolean zrleRecWarningShown = false;
+
+	// Zlib encoder's data.
+	byte[] zlibBuf;
+	int zlibBufLen = 0;
+	Inflater zlibInflater;
+
+	// Tight encoder's data.
+	final static int tightZlibBufferSize = 512;
+	Inflater[] tightInflaters;
+
+	// Since JPEG images are loaded asynchronously, we have to remember
+	// their position in the framebuffer. Also, this jpegRect object is
+	// used for synchronization between the rfbThread and a JVM's thread
+	// which decodes and loads JPEG images.
+	Rectangle jpegRect;
+
+	// True if we process keyboard and mouse events.
+	boolean inputEnabled;
+
+	//
+	// 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;
+
+		tightInflaters = new Inflater[4];
+
+		cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6));
+		cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
+
+		colors = new Color[256];
+		for (int i = 0; i < 256; i++)
+			colors[i] = new Color(cm8.getRGB(i));
+
+		setPixelFormat();
+
+		inputEnabled = false;
+		if (!viewer.options.viewOnly)
+			enableInput(true);
+
+		// Keyboard listener is enabled even in view-only mode, to catch
+		// 'r' or 'R' key presses used to request screen update.
+		addKeyListener(this);
+	}
+
+	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);
+	}
+
+	//
+	// Override the ImageObserver interface method to handle drawing of
+	// JPEG-encoded data.
+	//
+
+	public boolean imageUpdate(Image img, int infoflags, int x, int y,
+			int width, int height) {
+		if ((infoflags & (ALLBITS | ABORT)) == 0) {
+			return true; // We need more image data.
+		} else {
+			// If the whole image is available, draw it now.
+			if ((infoflags & ALLBITS) != 0) {
+				if (jpegRect != null) {
+					synchronized (jpegRect) {
+						memGraphics
+								.drawImage(img, jpegRect.x, jpegRect.y, null);
+						scheduleRepaint(jpegRect.x, jpegRect.y, jpegRect.width,
+								jpegRect.height);
+						jpegRect.notify();
+					}
+				}
+			}
+			return false; // All image data was processed.
+		}
+	}
+
+	//
+	// 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;
+			addMouseListener(this);
+			addMouseMotionListener(this);
+			if (viewer.showControls) {
+				viewer.buttonPanel.enableRemoteAccessControls(true);
+			}
+			createSoftCursor(); // scaled cursor
+		} else if (!enable && inputEnabled) {
+			inputEnabled = false;
+			removeMouseListener(this);
+			removeMouseMotionListener(this);
+			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 updateFramebufferSize() {
+
+		// Useful shortcuts.
+		int fbWidth = rfb.framebufferWidth;
+		int fbHeight = rfb.framebufferHeight;
+
+		// Calculate scaling factor for auto scaling.
+		if (maxWidth > 0 && maxHeight > 0) {
+			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();
+			}
+		}
+
+		// Images with raw pixels should be re-allocated on every change
+		// of geometry or pixel format.
+		if (bytesPixel == 1) {
+
+			pixels24 = null;
+			pixels8 = new byte[fbWidth * fbHeight];
+
+			pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm8,
+					pixels8, 0, fbWidth);
+
+			zrleTilePixels24 = null;
+			zrleTilePixels8 = new byte[64 * 64];
+
+		} else {
+
+			pixels8 = null;
+			pixels24 = new int[fbWidth * fbHeight];
+
+			pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm24,
+					pixels24, 0, fbWidth);
+
+			zrleTilePixels8 = null;
+			zrleTilePixels24 = new int[64 * 64];
+
+		}
+		pixelsSource.setAnimated(true);
+		rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource);
+
+		// Update the size of desktop containers.
+		if (viewer.inSeparateFrame) {
+			if (viewer.desktopScrollPane != null)
+				resizeDesktopFrame();
+		} 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
+		//
+
+		long count = 0;
+
+/*
+		rfb.selectPort();
+		try {
+			// rfb.setSoTimeout(1000);
+			Socket newCli = rfb.accept();
+			rfb.sendInitData(newCli);
+			rfb.addSock(newCli);
+		} catch (IOException e) {
+		}
+*/
+		 Thread accept = new Thread(new acceptThread(rfb)); 
+		 accept.start();
+
+		while (true) {
+
+			
+			if(!rfb.ready())continue;
+			System.out.println("\ncount=" + count);
+			count++;
+			System.out.println("ready rfb.available()="+rfb.available());			
+			
+			rfb.regiFramebufferUpdate();
+			rfb.printFramebufferUpdate();
+			rfb.checkAndMark();
+			
+			System.out.println("rfb.available()="+rfb.available());
+
+//			rfb.printNumBytesRead();
+			long bufSize = rfb.getNumBytesRead();			
+			// 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:
+						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;
+				}
+
+				// Request framebuffer update if needed.
+				int w = rfb.framebufferWidth;
+				int h = rfb.framebufferHeight;
+				rfb.writeFramebufferUpdateRequest(0, 0, w, h, !fullUpdateNeeded);
+
+				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);
+			}
+			bufSize = rfb.getNumBytesRead() - bufSize;
+			System.out.println("bufSize="+bufSize);
+			rfb.bufResetSend((int)bufSize);
+			if(rfb.available() < bufSize){
+				System.out.println("rfb.available() < bufSize");
+			
+			}
+						
+		}
+	}
+
+	//
+	// 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 {
+		handleRawRect(x, y, w, h, true);
+	}
+
+	void handleRawRect(int x, int y, int w, int h, boolean paint)
+			throws IOException {
+
+		if (bytesPixel == 1) {
+			for (int dy = y; dy < y + h; dy++) {
+				rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w);
+				if (rfb.rec != null) {
+					rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w);
+				}
+			}
+		} else {
+			byte[] buf = new byte[w * 4];
+			int i, offset;
+			for (int dy = y; dy < y + h; dy++) {
+				rfb.readFully(buf);
+				if (rfb.rec != null) {
+					rfb.rec.write(buf);
+				}
+				offset = dy * rfb.framebufferWidth + x;
+				for (i = 0; i < w; i++) {
+					pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16
+							| (buf[i * 4 + 1] & 0xFF) << 8
+							| (buf[i * 4] & 0xFF);
+				}
+			}
+		}
+
+		handleUpdatedPixels(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 {
+
+		rfb.readCopyRect();
+		memGraphics.copyArea(rfb.copyRectSrcX, rfb.copyRectSrcY, w, h, x
+				- rfb.copyRectSrcX, y - rfb.copyRectSrcY);
+
+		scheduleRepaint(x, y, w, h);
+	}
+
+	//
+	// Handle an RRE-encoded rectangle.
+	//
+
+	void handleRRERect(int x, int y, int w, int h) throws IOException {
+
+		int nSubrects = rfb.readU32();
+
+		byte[] bg_buf = new byte[bytesPixel];
+		rfb.readFully(bg_buf);
+		Color pixel;
+		if (bytesPixel == 1) {
+			pixel = colors[bg_buf[0] & 0xFF];
+		} else {
+			pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF,
+					bg_buf[0] & 0xFF);
+		}
+		memGraphics.setColor(pixel);
+		memGraphics.fillRect(x, y, w, h);
+
+		byte[] buf = new byte[nSubrects * (bytesPixel + 8)];
+		rfb.readFully(buf);
+		DataInputStream ds = new DataInputStream(new ByteArrayInputStream(buf));
+
+		if (rfb.rec != null) {
+			rfb.rec.writeIntBE(nSubrects);
+			rfb.rec.write(bg_buf);
+			rfb.rec.write(buf);
+		}
+
+		int sx, sy, sw, sh;
+
+		for (int j = 0; j < nSubrects; j++) {
+			if (bytesPixel == 1) {
+				pixel = colors[ds.readUnsignedByte()];
+			} else {
+				ds.skip(4);
+				pixel = new Color(buf[j * 12 + 2] & 0xFF,
+						buf[j * 12 + 1] & 0xFF, buf[j * 12] & 0xFF);
+			}
+			sx = x + ds.readUnsignedShort();
+			sy = y + ds.readUnsignedShort();
+			sw = ds.readUnsignedShort();
+			sh = ds.readUnsignedShort();
+
+			memGraphics.setColor(pixel);
+			memGraphics.fillRect(sx, sy, sw, sh);
+		}
+
+		scheduleRepaint(x, y, w, h);
+	}
+
+	//
+	// Handle a CoRRE-encoded rectangle.
+	//
+
+	void handleCoRRERect(int x, int y, int w, int h) throws IOException {
+		int nSubrects = rfb.readU32();
+
+		byte[] bg_buf = new byte[bytesPixel];
+		rfb.readFully(bg_buf);
+		Color pixel;
+		if (bytesPixel == 1) {
+			pixel = colors[bg_buf[0] & 0xFF];
+		} else {
+			pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF,
+					bg_buf[0] & 0xFF);
+		}
+		memGraphics.setColor(pixel);
+		memGraphics.fillRect(x, y, w, h);
+
+		byte[] buf = new byte[nSubrects * (bytesPixel + 4)];
+		rfb.readFully(buf);
+
+		if (rfb.rec != null) {
+			rfb.rec.writeIntBE(nSubrects);
+			rfb.rec.write(bg_buf);
+			rfb.rec.write(buf);
+		}
+
+		int sx, sy, sw, sh;
+		int i = 0;
+
+		for (int j = 0; j < nSubrects; j++) {
+			if (bytesPixel == 1) {
+				pixel = colors[buf[i++] & 0xFF];
+			} else {
+				pixel = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF,
+						buf[i] & 0xFF);
+				i += 4;
+			}
+			sx = x + (buf[i++] & 0xFF);
+			sy = y + (buf[i++] & 0xFF);
+			sw = buf[i++] & 0xFF;
+			sh = buf[i++] & 0xFF;
+
+			memGraphics.setColor(pixel);
+			memGraphics.fillRect(sx, sy, sw, sh);
+		}
+
+		scheduleRepaint(x, y, w, h);
+	}
+
+	//
+	// Handle a Hextile-encoded rectangle.
+	//
+
+	// These colors should be kept between handleHextileSubrect() calls.
+	private Color hextile_bg, hextile_fg;
+
+	void handleHextileRect(int x, int y, int w, int h) throws IOException {
+
+		hextile_bg = new Color(0);
+		hextile_fg = new Color(0);
+
+		for (int ty = y; ty < y + h; ty += 16) {
+			int th = 16;
+			if (y + h - ty < 16)
+				th = y + h - ty;
+
+			for (int tx = x; tx < x + w; tx += 16) {
+				int tw = 16;
+				if (x + w - tx < 16)
+					tw = x + w - tx;
+
+				handleHextileSubrect(tx, ty, tw, th);
+			}
+
+			// Finished with a row of tiles, now let's show it.
+			scheduleRepaint(x, y, w, h);
+		}
+	}
+
+	//
+	// Handle one tile in the Hextile-encoded data.
+	//
+
+	void handleHextileSubrect(int tx, int ty, int tw, int th)
+			throws IOException {
+
+		int subencoding = rfb.readU8();
+		if (rfb.rec != null) {
+			rfb.rec.writeByte(subencoding);
+		}
+
+		// Is it a raw-encoded sub-rectangle?
+		if ((subencoding & rfb.HextileRaw) != 0) {
+			handleRawRect(tx, ty, tw, th, false);
+			return;
+		}
+
+		// Read and draw the background if specified.
+		byte[] cbuf = new byte[bytesPixel];
+		if ((subencoding & rfb.HextileBackgroundSpecified) != 0) {
+			rfb.readFully(cbuf);
+			if (bytesPixel == 1) {
+				hextile_bg = colors[cbuf[0] & 0xFF];
+			} else {
+				hextile_bg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF,
+						cbuf[0] & 0xFF);
+			}
+			if (rfb.rec != null) {
+				rfb.rec.write(cbuf);
+			}
+		}
+		memGraphics.setColor(hextile_bg);
+		memGraphics.fillRect(tx, ty, tw, th);
+
+		// Read the foreground color if specified.
+		if ((subencoding & rfb.HextileForegroundSpecified) != 0) {
+			rfb.readFully(cbuf);
+			if (bytesPixel == 1) {
+				hextile_fg = colors[cbuf[0] & 0xFF];
+			} else {
+				hextile_fg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF,
+						cbuf[0] & 0xFF);
+			}
+			if (rfb.rec != null) {
+				rfb.rec.write(cbuf);
+			}
+		}
+
+		// Done with this tile if there is no sub-rectangles.
+		if ((subencoding & rfb.HextileAnySubrects) == 0)
+			return;
+
+		int nSubrects = rfb.readU8();
+		int bufsize = nSubrects * 2;
+		if ((subencoding & rfb.HextileSubrectsColoured) != 0) {
+			bufsize += nSubrects * bytesPixel;
+		}
+		byte[] buf = new byte[bufsize];
+		rfb.readFully(buf);
+		if (rfb.rec != null) {
+			rfb.rec.writeByte(nSubrects);
+			rfb.rec.write(buf);
+		}
+
+		int b1, b2, sx, sy, sw, sh;
+		int i = 0;
+
+		if ((subencoding & rfb.HextileSubrectsColoured) == 0) {
+
+			// Sub-rectangles are all of the same color.
+			memGraphics.setColor(hextile_fg);
+			for (int j = 0; j < nSubrects; j++) {
+				b1 = buf[i++] & 0xFF;
+				b2 = buf[i++] & 0xFF;
+				sx = tx + (b1 >> 4);
+				sy = ty + (b1 & 0xf);
+				sw = (b2 >> 4) + 1;
+				sh = (b2 & 0xf) + 1;
+				memGraphics.fillRect(sx, sy, sw, sh);
+			}
+		} else if (bytesPixel == 1) {
+
+			// BGR233 (8-bit color) version for colored sub-rectangles.
+			for (int j = 0; j < nSubrects; j++) {
+				hextile_fg = colors[buf[i++] & 0xFF];
+				b1 = buf[i++] & 0xFF;
+				b2 = buf[i++] & 0xFF;
+				sx = tx + (b1 >> 4);
+				sy = ty + (b1 & 0xf);
+				sw = (b2 >> 4) + 1;
+				sh = (b2 & 0xf) + 1;
+				memGraphics.setColor(hextile_fg);
+				memGraphics.fillRect(sx, sy, sw, sh);
+			}
+
+		} else {
+
+			// Full-color (24-bit) version for colored sub-rectangles.
+			for (int j = 0; j < nSubrects; j++) {
+				hextile_fg = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF,
+						buf[i] & 0xFF);
+				i += 4;
+				b1 = buf[i++] & 0xFF;
+				b2 = buf[i++] & 0xFF;
+				sx = tx + (b1 >> 4);
+				sy = ty + (b1 & 0xf);
+				sw = (b2 >> 4) + 1;
+				sh = (b2 & 0xf) + 1;
+				memGraphics.setColor(hextile_fg);
+				memGraphics.fillRect(sx, sy, sw, sh);
+			}
+
+		}
+	}
+
+	//
+	// 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 {
+
+		if (zrleInStream == null)
+			zrleInStream = new ZlibInStream();
+//		System.out.println("zrleInStream.end="+zrleInStream.inflater.off);			
+		
+		int nBytes = rfb.readU32();
+		if (nBytes > 64 * 1024 * 1024)
+			throw new Exception("ZRLE decoder: illegal compressed data size");
+
+		if (zrleBuf == null || zrleBufLen < nBytes) {
+			zrleBufLen = nBytes + 4096;
+			zrleBuf = new byte[zrleBufLen];
+		}
+
+		// FIXME: Do not wait for all the data before decompression.
+		rfb.readFully(zrleBuf, 0, nBytes);
+
+		if (rfb.rec != null) {
+			if (rfb.recordFromBeginning) {
+				rfb.rec.writeIntBE(nBytes);
+				rfb.rec.write(zrleBuf, 0, nBytes);
+			} else if (!zrleRecWarningShown) {
+				System.out.println("Warning: ZRLE session can be recorded"
+						+ " only from the beginning");
+				System.out.println("Warning: Recorded file may be corrupted");
+				zrleRecWarningShown = true;
+			}
+
+		}
+
+		zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes);
+
+		for (int ty = y; ty < y + h; ty += 64) {
+
+			int th = Math.min(y + h - ty, 64);
+
+			for (int tx = x; tx < x + w; tx += 64) {
+
+				int tw = Math.min(x + w - tx, 64);
+
+				int mode = zrleInStream.readU8();
+				boolean rle = (mode & 128) != 0;
+				int palSize = mode & 127;
+				int[] palette = new int[128];
+
+				readZrlePalette(palette, palSize);
+
+				if (palSize == 1) {
+					int pix = palette[0];
+					Color c = (bytesPixel == 1) ? colors[pix] : new Color(
+							0xFF000000 | pix);
+					memGraphics.setColor(c);
+					memGraphics.fillRect(tx, ty, tw, th);
+					continue;
+				}
+
+				if (!rle) {
+					if (palSize == 0) {
+						readZrleRawPixels(tw, th);
+					} else {
+						readZrlePackedPixels(tw, th, palette, palSize);
+					}
+				} else {
+					if (palSize == 0) {
+						readZrlePlainRLEPixels(tw, th);
+					} else {
+						readZrlePackedRLEPixels(tw, th, palette);
+					}
+				}
+				handleUpdatedZrleTile(tx, ty, tw, th);
+			}
+		}
+
+		zrleInStream.reset();
+
+		scheduleRepaint(x, y, w, h);
+	}
+
+	int readPixel(InStream is) throws Exception {
+		int pix;
+
+		if (bytesPixel == 1) {
+
+			pix = is.readU8();
+		} else {
+			int p1 = is.readU8();
+			int p2 = is.readU8();
+			int p3 = is.readU8();
+			pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF);
+		}
+		return pix;
+	}
+
+	void readPixels(InStream is, int[] dst, int count) throws Exception {
+		int pix;
+		if (bytesPixel == 1) {
+			byte[] buf = new byte[count];
+			is.readBytes(buf, 0, count);
+			for (int i = 0; i < count; i++) {
+				dst[i] = (int) buf[i] & 0xFF;
+			}
+		} else {
+			byte[] buf = new byte[count * 3];
+			is.readBytes(buf, 0, count * 3);
+			for (int i = 0; i < count; i++) {
+				dst[i] = ((buf[i * 3 + 2] & 0xFF) << 16
+						| (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3] & 0xFF));
+			}
+		}
+	}
+
+	void readZrlePalette(int[] palette, int palSize) throws Exception {
+		readPixels(zrleInStream, palette, palSize);
+	}
+
+	void readZrleRawPixels(int tw, int th) throws Exception {
+		if (bytesPixel == 1) {
+			zrleInStream.readBytes(zrleTilePixels8, 0, tw * th);
+		} else {
+			readPixels(zrleInStream, zrleTilePixels24, tw * th); // /
+		}
+	}
+
+	void readZrlePackedPixels(int tw, int th, int[] palette, int palSize)
+			throws Exception {
+
+		int bppp = ((palSize > 16) ? 8 : ((palSize > 4) ? 4
+				: ((palSize > 2) ? 2 : 1)));
+		int ptr = 0;
+
+		for (int i = 0; i < th; i++) {
+			int eol = ptr + tw;
+			int b = 0;
+			int nbits = 0;
+
+			while (ptr < eol) {
+				if (nbits == 0) {
+					b = zrleInStream.readU8();
+					nbits = 8;
+				}
+				nbits -= bppp;
+				int index = (b >> nbits) & ((1 << bppp) - 1) & 127;
+				if (bytesPixel == 1) {
+					zrleTilePixels8[ptr++] = (byte) palette[index];
+				} else {
+					zrleTilePixels24[ptr++] = palette[index];
+				}
+			}
+		}
+	}
+
+	void readZrlePlainRLEPixels(int tw, int th) throws Exception {
+		int ptr = 0;
+		int end = ptr + tw * th;
+		while (ptr < end) {
+			int pix = readPixel(zrleInStream);
+			int len = 1;
+			int b;
+			do {
+				b = zrleInStream.readU8();
+				len += b;
+			} while (b == 255);
+
+			if (!(len <= end - ptr))
+				throw new Exception("ZRLE decoder: assertion failed"
+						+ " (len <= end-ptr)");
+
+			if (bytesPixel == 1) {
+				while (len-- > 0)
+					zrleTilePixels8[ptr++] = (byte) pix;
+			} else {
+				while (len-- > 0)
+					zrleTilePixels24[ptr++] = pix;
+			}
+		}
+	}
+
+	void readZrlePackedRLEPixels(int tw, int th, int[] palette)
+			throws Exception {
+
+		int ptr = 0;
+		int end = ptr + tw * th;
+		while (ptr < end) {
+			int index = zrleInStream.readU8();
+			int len = 1;
+			if ((index & 128) != 0) {
+				int b;
+				do {
+					b = zrleInStream.readU8();
+					len += b;
+				} while (b == 255);
+
+				if (!(len <= end - ptr))
+					throw new Exception("ZRLE decoder: assertion failed"
+							+ " (len <= end - ptr)");
+			}
+
+			index &= 127;
+			int pix = palette[index];
+
+			if (bytesPixel == 1) {
+				while (len-- > 0)
+					zrleTilePixels8[ptr++] = (byte) pix;
+			} else {
+				while (len-- > 0)
+					zrleTilePixels24[ptr++] = pix;
+			}
+		}
+	}
+
+	//
+	// Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update.
+	//
+
+	void handleUpdatedZrleTile(int x, int y, int w, int h) {
+		Object src, dst;
+		if (bytesPixel == 1) {
+			src = zrleTilePixels8;
+			dst = pixels8;
+		} else {
+			src = zrleTilePixels24;
+			dst = pixels24;
+		}
+		int offsetSrc = 0;
+		int offsetDst = (y * rfb.framebufferWidth + x);
+		for (int j = 0; j < h; j++) {
+			System.arraycopy(src, offsetSrc, dst, offsetDst, w);
+			offsetSrc += w;
+			offsetDst += rfb.framebufferWidth;
+		}
+		handleUpdatedPixels(x, y, w, h);
+	}
+
+	//
+	// Handle a Zlib-encoded rectangle.
+	//
+
+	void handleZlibRect(int x, int y, int w, int h) throws Exception {
+
+		int nBytes = rfb.readU32();
+
+		if (zlibBuf == null || zlibBufLen < nBytes) {
+			zlibBufLen = nBytes * 2;
+			zlibBuf = new byte[zlibBufLen];
+		}
+
+		rfb.readFully(zlibBuf, 0, nBytes);
+
+		if (rfb.rec != null && rfb.recordFromBeginning) {
+			rfb.rec.writeIntBE(nBytes);
+			rfb.rec.write(zlibBuf, 0, nBytes);
+		}
+
+		if (zlibInflater == null) {
+			zlibInflater = new Inflater();
+		}
+		zlibInflater.setInput(zlibBuf, 0, nBytes);
+
+		if (bytesPixel == 1) {
+			for (int dy = y; dy < y + h; dy++) {
+				zlibInflater.inflate(pixels8, dy * rfb.framebufferWidth + x, w);
+				if (rfb.rec != null && !rfb.recordFromBeginning)
+					rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w);
+			}
+		} else {
+			byte[] buf = new byte[w * 4];
+			int i, offset;
+			for (int dy = y; dy < y + h; dy++) {
+				zlibInflater.inflate(buf);
+				offset = dy * rfb.framebufferWidth + x;
+				for (i = 0; i < w; i++) {
+					pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16
+							| (buf[i * 4 + 1] & 0xFF) << 8
+							| (buf[i * 4] & 0xFF);
+				}
+				if (rfb.rec != null && !rfb.recordFromBeginning)
+					rfb.rec.write(buf);
+			}
+		}
+
+		handleUpdatedPixels(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 {
+
+		int comp_ctl = rfb.readU8();
+		if (rfb.rec != null) {
+			if (rfb.recordFromBeginning || comp_ctl == (rfb.TightFill << 4)
+					|| comp_ctl == (rfb.TightJpeg << 4)) {
+				// Send data exactly as received.
+				rfb.rec.writeByte(comp_ctl);
+			} else {
+				// Tell the decoder to flush each of the four zlib streams.
+				rfb.rec.writeByte(comp_ctl | 0x0F);
+			}
+		}
+
+		// Flush zlib streams if we are told by the server to do so.
+		for (int stream_id = 0; stream_id < 4; stream_id++) {
+			if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) {
+				tightInflaters[stream_id] = null;
+			}
+			comp_ctl >>= 1;
+		}
+
+		// Check correctness of subencoding value.
+		if (comp_ctl > rfb.TightMaxSubencoding) {
+			throw new Exception("Incorrect tight subencoding: " + comp_ctl);
+		}
+
+		// Handle solid-color rectangles.
+		if (comp_ctl == rfb.TightFill) {
+
+			if (bytesPixel == 1) {
+				int idx = rfb.readU8();
+				memGraphics.setColor(colors[idx]);
+				if (rfb.rec != null) {
+					rfb.rec.writeByte(idx);
+				}
+			} else {
+				byte[] buf = new byte[3];
+				rfb.readFully(buf);
+				if (rfb.rec != null) {
+					rfb.rec.write(buf);
+				}
+				Color bg = new Color(0xFF000000 | (buf[0] & 0xFF) << 16
+						| (buf[1] & 0xFF) << 8 | (buf[2] & 0xFF));
+				memGraphics.setColor(bg);
+			}
+			memGraphics.fillRect(x, y, w, h);
+			scheduleRepaint(x, y, w, h);
+			return;
+
+		}
+
+		if (comp_ctl == rfb.TightJpeg) {
+
+			statNumRectsTightJPEG++;
+
+			// Read JPEG data.
+			byte[] jpegData = new byte[rfb.readCompactLen()];
+			rfb.readFully(jpegData);
+			if (rfb.rec != null) {
+				if (!rfb.recordFromBeginning) {
+					rfb.recordCompactLen(jpegData.length);
+				}
+				rfb.rec.write(jpegData);
+			}
+
+			// Create an Image object from the JPEG data.
+			Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData);
+
+			// Remember the rectangle where the image should be drawn.
+			jpegRect = new Rectangle(x, y, w, h);
+
+			// Let the imageUpdate() method do the actual drawing, here just
+			// wait until the image is fully loaded and drawn.
+			synchronized (jpegRect) {
+				Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1,
+						this);
+				try {
+					// Wait no longer than three seconds.
+					jpegRect.wait(3000);
+				} catch (InterruptedException e) {
+					throw new Exception("Interrupted while decoding JPEG image");
+				}
+			}
+
+			// Done, jpegRect is not needed any more.
+			jpegRect = null;
+			return;
+
+		}
+
+		// Read filter id and parameters.
+		int numColors = 0, rowSize = w;
+		byte[] palette8 = new byte[2];
+		int[] palette24 = new int[256];
+		boolean useGradient = false;
+		if ((comp_ctl & rfb.TightExplicitFilter) != 0) {
+			int filter_id = rfb.readU8();
+			if (rfb.rec != null) {
+				rfb.rec.writeByte(filter_id);
+			}
+			if (filter_id == rfb.TightFilterPalette) {
+				numColors = rfb.readU8() + 1;
+				if (rfb.rec != null) {
+					rfb.rec.writeByte(numColors - 1);
+				}
+				if (bytesPixel == 1) {
+					if (numColors != 2) {
+						throw new Exception("Incorrect tight palette size: "
+								+ numColors);
+					}
+					rfb.readFully(palette8);
+					if (rfb.rec != null) {
+						rfb.rec.write(palette8);
+					}
+				} else {
+					byte[] buf = new byte[numColors * 3];
+					rfb.readFully(buf);
+					if (rfb.rec != null) {
+						rfb.rec.write(buf);
+					}
+					for (int i = 0; i < numColors; i++) {
+						palette24[i] = ((buf[i * 3] & 0xFF) << 16
+								| (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3 + 2] & 0xFF));
+					}
+				}
+				if (numColors == 2)
+					rowSize = (w + 7) / 8;
+			} else if (filter_id == rfb.TightFilterGradient) {
+				useGradient = true;
+			} else if (filter_id != rfb.TightFilterCopy) {
+				throw new Exception("Incorrect tight filter id: " + filter_id);
+			}
+		}
+		if (numColors == 0 && bytesPixel == 4)
+			rowSize *= 3;
+
+		// Read, optionally uncompress and decode data.
+		int dataSize = h * rowSize;
+		if (dataSize < rfb.TightMinToCompress) {
+			// Data size is small - not compressed with zlib.
+			if (numColors != 0) {
+				// Indexed colors.
+				byte[] indexedData = new byte[dataSize];
+				rfb.readFully(indexedData);
+				if (rfb.rec != null) {
+					rfb.rec.write(indexedData);
+				}
+				if (numColors == 2) {
+					// Two colors.
+					if (bytesPixel == 1) {
+						decodeMonoData(x, y, w, h, indexedData, palette8);
+					} else {
+						decodeMonoData(x, y, w, h, indexedData, palette24);
+					}
+				} else {
+					// 3..255 colors (assuming bytesPixel == 4).
+					int i = 0;
+					for (int dy = y; dy < y + h; dy++) {
+						for (int dx = x; dx < x + w; dx++) {
+							pixels24[dy * rfb.framebufferWidth + dx] = palette24[indexedData[i++] & 0xFF];
+						}
+					}
+				}
+			} else if (useGradient) {
+				// "Gradient"-processed data
+				byte[] buf = new byte[w * h * 3];
+				rfb.readFully(buf);
+				if (rfb.rec != null) {
+					rfb.rec.write(buf);
+				}
+				decodeGradientData(x, y, w, h, buf);
+			} else {
+				// Raw truecolor data.
+				if (bytesPixel == 1) {
+					for (int dy = y; dy < y + h; dy++) {
+						rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w);
+						if (rfb.rec != null) {
+							rfb.rec.write(pixels8, dy * rfb.framebufferWidth
+									+ x, w);
+						}
+					}
+				} else {
+					byte[] buf = new byte[w * 3];
+					int i, offset;
+					for (int dy = y; dy < y + h; dy++) {
+						rfb.readFully(buf);
+						if (rfb.rec != null) {
+							rfb.rec.write(buf);
+						}
+						offset = dy * rfb.framebufferWidth + x;
+						for (i = 0; i < w; i++) {
+							pixels24[offset + i] = (buf[i * 3] & 0xFF) << 16
+									| (buf[i * 3 + 1] & 0xFF) << 8
+									| (buf[i * 3 + 2] & 0xFF);
+						}
+					}
+				}
+			}
+		} else {
+			// Data was compressed with zlib.
+			int zlibDataLen = rfb.readCompactLen();
+			byte[] zlibData = new byte[zlibDataLen];
+			rfb.readFully(zlibData);
+			if (rfb.rec != null && rfb.recordFromBeginning) {
+				rfb.rec.write(zlibData);
+			}
+			int stream_id = comp_ctl & 0x03;
+			if (tightInflaters[stream_id] == null) {
+				tightInflaters[stream_id] = new Inflater();
+			}
+			Inflater myInflater = tightInflaters[stream_id];
+			myInflater.setInput(zlibData);
+			byte[] buf = new byte[dataSize];
+			myInflater.inflate(buf);
+			if (rfb.rec != null && !rfb.recordFromBeginning) {
+				rfb.recordCompressedData(buf);
+			}
+
+			if (numColors != 0) {
+				// Indexed colors.
+				if (numColors == 2) {
+					// Two colors.
+					if (bytesPixel == 1) {
+						decodeMonoData(x, y, w, h, buf, palette8);
+					} else {
+						decodeMonoData(x, y, w, h, buf, palette24);
+					}
+				} else {
+					// More than two colors (assuming bytesPixel == 4).
+					int i = 0;
+					for (int dy = y; dy < y + h; dy++) {
+						for (int dx = x; dx < x + w; dx++) {
+							pixels24[dy * rfb.framebufferWidth + dx] = palette24[buf[i++] & 0xFF];
+						}
+					}
+				}
+			} else if (useGradient) {
+				// Compressed "Gradient"-filtered data (assuming bytesPixel ==
+				// 4).
+				decodeGradientData(x, y, w, h, buf);
+			} else {
+				// Compressed truecolor data.
+				if (bytesPixel == 1) {
+					int destOffset = y * rfb.framebufferWidth + x;
+					for (int dy = 0; dy < h; dy++) {
+						System.arraycopy(buf, dy * w, pixels8, destOffset, w);
+						destOffset += rfb.framebufferWidth;
+					}
+				} else {
+					int srcOffset = 0;
+					int destOffset, i;
+					for (int dy = 0; dy < h; dy++) {
+						myInflater.inflate(buf);
+						destOffset = (y + dy) * rfb.framebufferWidth + x;
+						for (i = 0; i < w; i++) {
+							pixels24[destOffset + i] = (buf[srcOffset] & 0xFF) << 16
+									| (buf[srcOffset + 1] & 0xFF) << 8
+									| (buf[srcOffset + 2] & 0xFF);
+							srcOffset += 3;
+						}
+					}
+				}
+			}
+		}
+
+		handleUpdatedPixels(x, y, w, h);
+		scheduleRepaint(x, y, w, h);
+	}
+
+	//
+	// Decode 1bpp-encoded bi-color rectangle (8-bit and 24-bit versions).
+	//
+
+	void decodeMonoData(int x, int y, int w, int h, byte[] src, byte[] palette) {
+
+		int dx, dy, n;
+		int i = y * rfb.framebufferWidth + x;
+		int rowBytes = (w + 7) / 8;
+		byte b;
+
+		for (dy = 0; dy < h; dy++) {
+			for (dx = 0; dx < w / 8; dx++) {
+				b = src[dy * rowBytes + dx];
+				for (n = 7; n >= 0; n--)
+					pixels8[i++] = palette[b >> n & 1];
+			}
+			for (n = 7; n >= 8 - w % 8; n--) {
+				pixels8[i++] = palette[src[dy * rowBytes + dx] >> n & 1];
+			}
+			i += (rfb.framebufferWidth - w);
+		}
+	}
+
+	void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette) {
+
+		int dx, dy, n;
+		int i = y * rfb.framebufferWidth + x;
+		int rowBytes = (w + 7) / 8;
+		byte b;
+
+		for (dy = 0; dy < h; dy++) {
+			for (dx = 0; dx < w / 8; dx++) {
+				b = src[dy * rowBytes + dx];
+				for (n = 7; n >= 0; n--)
+					pixels24[i++] = palette[b >> n & 1];
+			}
+			for (n = 7; n >= 8 - w % 8; n--) {
+				pixels24[i++] = palette[src[dy * rowBytes + dx] >> n & 1];
+			}
+			i += (rfb.framebufferWidth - w);
+		}
+	}
+
+	//
+	// Decode data processed with the "Gradient" filter.
+	//
+
+	void decodeGradientData(int x, int y, int w, int h, byte[] buf) {
+
+		int dx, dy, c;
+		byte[] prevRow = new byte[w * 3];
+		byte[] thisRow = new byte[w * 3];
+		byte[] pix = new byte[3];
+		int[] est = new int[3];
+
+		int offset = y * rfb.framebufferWidth + x;
+
+		for (dy = 0; dy < h; dy++) {
+
+			/* First pixel in a row */
+			for (c = 0; c < 3; c++) {
+				pix[c] = (byte) (prevRow[c] + buf[dy * w * 3 + c]);
+				thisRow[c] = pix[c];
+			}
+			pixels24[offset++] = (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8
+					| (pix[2] & 0xFF);
+
+			/* Remaining pixels of a row */
+			for (dx = 1; dx < w; dx++) {
+				for (c = 0; c < 3; c++) {
+					est[c] = ((prevRow[dx * 3 + c] & 0xFF) + (pix[c] & 0xFF) - (prevRow[(dx - 1)
+							* 3 + c] & 0xFF));
+					if (est[c] > 0xFF) {
+						est[c] = 0xFF;
+					} else if (est[c] < 0x00) {
+						est[c] = 0x00;
+					}
+					pix[c] = (byte) (est[c] + buf[(dy * w + dx) * 3 + c]);
+					thisRow[dx * 3 + c] = pix[c];
+				}
+				pixels24[offset++] = (pix[0] & 0xFF) << 16
+						| (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
+			}
+
+			System.arraycopy(thisRow, 0, prevRow, 0, w * 3);
+			offset += (rfb.framebufferWidth - w);
+		}
+	}
+
+	//
+	// Display newly updated area of pixels.
+	//
+
+	void handleUpdatedPixels(int x, int y, int w, int h) {
+
+		// Draw updated pixels of the off-screen image.
+		pixelsSource.newPixels(x, y, w, h);
+		memGraphics.setClip(x, y, w, h);
+		memGraphics.drawImage(rawPixelsImage, 0, 0, null);
+		memGraphics.setClip(0, 0, rfb.framebufferWidth, rfb.framebufferHeight);
+	}
+
+	//
+	// Tell JVM to repaint specified desktop area.
+	//
+
+	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);
+	}
+
+	public 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();
+	}
+
+	public void processLocalMouseEvent(MouseEvent evt, boolean moved) {
+		if (viewer.rfb != null && rfb.inNormalProtocol) {
+			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();
+			}
+		}
+	}
+
+	//
+	// Ignored events.
+	//
+
+	public void mouseClicked(MouseEvent evt) {
+	}
+
+	public void mouseEntered(MouseEvent evt) {
+	}
+
+	public void mouseExited(MouseEvent evt) {
+	}
+
+	//
+	// 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;
+	}
+
+	// ////////////////////////////////////////////////////////////////
+	//
+	// Handle cursor shape updates (XCursor and RichCursor encodings).
+	//
+
+	boolean showSoftCursor = false;
+
+	MemoryImageSource softCursorSource;
+	Image softCursor;
+
+	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 * bytesPixel + 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 pixByte, 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);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/VncCanvas2.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,64 @@
+package myVncProxy;
+//
+//  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.
+//
+
+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) {}
+  }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/VncProxyService.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,766 @@
+package myVncProxy;
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.net.*;
+
+public class VncProxyService implements java.lang.Runnable {
+
+	public static void main(String[] argv) {
+		VncProxyService v = new VncProxyService();
+		v.mainArgs = argv;
+
+		v.init();
+
+	}
+	
+	String[] mainArgs;
+
+	// RfbProto rfb;
+	MyRfbProto rfb;
+	Thread rfbThread;
+
+	Frame vncFrame;
+	Container vncContainer;
+	ScrollPane desktopScrollPane;
+	GridBagLayout gridbag;
+	ButtonPanel buttonPanel;
+	Label connStatusLabel;
+	ProxyVncCanvas 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;
+
+	//
+	// init()
+	//
+
+	public void init() {
+
+		readParameters();
+
+		recordingSync = new Object();
+
+		sessionFileName = null;
+		recordingActive = false;
+		recordingStatusChanged = false;
+		cursorUpdatesDef = null;
+		eightBitColorsDef = null;
+
+		rfbThread = new Thread(this);
+		rfbThread.start();
+		
+	}
+
+	//
+	// run() - executed by the rfbThread to deal with the RFB socket.
+	//
+
+	public void run() {
+
+		try {
+			connectAndAuthenticate();
+			doProtocolInitialisation();
+
+			vc = new ProxyVncCanvas(this, 0, 0);
+
+			processNormalProtocol();// main loop
+
+		} 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 (rfb != null && !rfb.closed())
+					rfb.close();
+				if (showControls && buttonPanel != null) {
+					buttonPanel.disableButtonsOnDisconnect();
+				}
+			} 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);
+			}
+		}
+
+	}
+
+	//
+	// 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();// main loop
+		} 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...");
+
+		showConnectionStatus("Connecting to " + host + ", port " + port + "...");
+
+		// rfb = new RfbProto(host, port, this);
+		rfb = new MyRfbProto(host, port);
+		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();
+		int authType;
+		if (secType == RfbProto.SecTypeTight) {
+			showConnectionStatus("Enabling TightVNC protocol extensions");
+			rfb.setupTunneling();
+			authType = rfb.negotiateAuthenticationTight();
+		} else {
+			authType = secType;
+		}
+
+		switch (authType) {
+		case RfbProto.AuthNone:
+			showConnectionStatus("No authentication needed");
+			rfb.authenticateNone();
+			break;
+		case RfbProto.AuthVNC:
+			showConnectionStatus("Performing standard VNC authentication");
+			if (passwordParam != null) {
+				rfb.authenticateVNC(passwordParam);
+			} else {
+				String pw = askPassword();
+				rfb.authenticateVNC(pw);
+			}
+			break;
+		default:
+			throw new Exception("Unknown authentication scheme " + authType);
+		}
+	}
+
+	//
+	// Show a message describing the connection status.
+	// To hide the connection status label, use (msg == null).
+	//
+
+	void showConnectionStatus(String msg) {
+		System.out.println(msg);
+	}
+
+	//
+	// Show an authentication panel.
+	//
+
+	String askPassword() throws Exception {
+		showConnectionStatus(null);
+		/*
+		 * AuthPanel authPanel = new AuthPanel(this);
+		 * 
+		 * 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);
+
+
+		authPanel.moveFocusToDefaultField();
+		vncContainer.remove(authPanel);
+		 */
+		String pw = mainArgs[2];
+		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();
+			}
+
+			System.out.println("Recording the session in " + sessionFileName);
+			rfb.startSession(sessionFileName);
+			recordingActive = true;
+		}
+	}
+
+	//
+	// 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() {
+
+		if (mainArgs.length > 0)
+			host = mainArgs[0];
+		else
+			host = "hades.cr.ie.u-ryukyu.ac.jp";
+
+		if (mainArgs.length > 1)
+			port = Integer.parseInt(mainArgs[1]);
+		else
+			port = 5900;
+
+		// Read "ENCPASSWORD" or "PASSWORD" parameter if specified.
+//		readPasswordParameters();
+
+		String str;
+
+		// "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);
+		String encPasswordParam = mainArgs[2];
+
+		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) {
+		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;
+	}
+
+	//
+	// 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;
+			int nRealRects = vc.statNumPixelRects;
+			int nPseudoRects = vc.statNumTotalRects - vc.statNumPixelRects;
+			System.out.println("Updates received: " + vc.statNumUpdates + " ("
+					+ nRealRects + " rectangles + " + nPseudoRects
+					+ " pseudo), " + rate + " updates/sec");
+			int 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);
+
+			int raw = vc.statNumBytesDecoded;
+			int 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();
+
+		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);
+		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();
+
+		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);
+			 */
+		}
+
+	}
+
+	//
+	// 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();
+	}
+
+	//
+	// Start/stop receiving mouse events.
+	//
+
+	public void enableInput(boolean enable) {
+		vc.enableInput(enable);
+	}
+
+	//
+	// Close application properly on window close event.
+	//
+
+	public void windowClosing(WindowEvent evt) {
+		System.out.println("Closing window");
+		if (rfb != null)
+			disconnect();
+
+		vncContainer.hide();
+
+	}
+
+	//
+	// 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) {
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/VncViewer.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,1040 @@
+package myVncProxy;
+//
+//  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.
+//
+
+//
+// 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.
+//
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.net.*;
+
+public class VncViewer extends java.applet.Applet implements
+		java.lang.Runnable, WindowListener {
+
+	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 static void main(String[] argv) {
+		VncViewer v = new VncViewer();
+		v.mainArgs = argv;
+		v.inAnApplet = false;
+		v.inSeparateFrame = true;
+		/*
+		 * if(argv.length > 1){ v.host = argv[0]; v.port =
+		 * Integer.parseInt(argv[1]); }
+		 */
+		v.init();
+		v.start();
+	}
+
+	String[] mainArgs;
+
+	// RfbProto rfb;
+	MyRfbProto 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("TightVNC");
+			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);
+
+		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);
+			} 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);
+
+				// 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();
+
+			}
+
+			if (showControls)
+				buttonPanel.enableButtons();
+
+			moveFocusToDesktop();
+			processNormalProtocol();// main loop
+
+		} 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);
+			}
+		}
+
+	}
+
+	//
+	// 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("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();// main loop
+		} 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.setVisible(true);
+		} else {
+			validate();
+		}
+
+		showConnectionStatus("Connecting to " + host + ", port " + port + "...");
+
+		// rfb = new RfbProto(host, port, this);
+		rfb = new MyRfbProto(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();
+		int authType;
+		if (secType == RfbProto.SecTypeTight) {
+			showConnectionStatus("Enabling TightVNC protocol extensions");
+			rfb.setupTunneling();
+			authType = rfb.negotiateAuthenticationTight();
+		} else {
+			authType = secType;
+		}
+
+		switch (authType) {
+		case RfbProto.AuthNone:
+			showConnectionStatus("No authentication needed");
+			rfb.authenticateNone();
+			break;
+		case RfbProto.AuthVNC:
+			showConnectionStatus("Performing standard VNC authentication");
+			if (passwordParam != null) {
+				rfb.authenticateVNC(passwordParam);
+			} else {
+				String pw = askPassword();
+				rfb.authenticateVNC(pw);
+			}
+			break;
+		default:
+			throw new Exception("Unknown authentication scheme " + authType);
+		}
+	}
+
+	//
+	// 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 askPassword() throws Exception {
+		showConnectionStatus(null);
+
+		AuthPanel authPanel = new AuthPanel(this);
+
+		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();
+
+/*
+		if (rfb.MYVNC) {
+			rfb.initServSock(5550);
+
+			try {
+				Socket newCli = rfb.accept();
+				rfb.sendInitData(newCli);
+				rfb.addSock(newCli);
+			} catch (IOException e) {
+			}
+
+		}
+*/
+
+		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();
+			}
+
+			System.out.println("Recording the session in " + sessionFileName);
+			rfb.startSession(sessionFileName);
+			recordingActive = true;
+		}
+	}
+
+	//
+	// 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 (mainArgs.length > 0)
+			host = mainArgs[0];
+		else
+			host = "hades.cr.ie.u-ryukyu.ac.jp";
+		/*
+		 * if (host == null) { host = getCodeBase().getHost(); if
+		 * (host.equals("")) { fatalError("HOST parameter not specified"); } }
+		 */
+
+		// port = readIntParameter("PORT", 5900);
+		if (mainArgs.length > 1)
+			port = Integer.parseInt(mainArgs[1]);
+		else
+			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;
+			int nRealRects = vc.statNumPixelRects;
+			int nPseudoRects = vc.statNumTotalRects - vc.statNumPixelRects;
+			System.out.println("Updates received: " + vc.statNumUpdates + " ("
+					+ nRealRects + " rectangles + " + nPseudoRects
+					+ " pseudo), " + rate + " updates/sec");
+			int 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);
+
+			int raw = vc.statNumBytesDecoded;
+			int 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);
+	}
+
+	//
+	// Close application properly on window close event.
+	//
+
+	public void windowClosing(WindowEvent evt) {
+		System.out.println("Closing window");
+		if (rfb != null)
+			disconnect();
+
+		vncContainer.hide();
+
+		if (!inAnApplet) {
+			System.exit(0);
+		}
+	}
+
+	//
+	// 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) {
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/ZlibInStream.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,113 @@
+package myVncProxy;
+/* 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
+//
+
+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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/acceptThread.java	Sun Apr 24 23:03:00 2011 +0900
@@ -0,0 +1,27 @@
+package myVncProxy;
+import java.net.BindException;
+import java.net.Socket;
+import java.io.IOException;
+
+public class acceptThread implements Runnable {
+	MyRfbProto rfb;
+
+	acceptThread(MyRfbProto _rfb) {
+		rfb = _rfb;
+	}
+
+
+	public void run() {
+		rfb.selectPort();
+		
+		while (true) {
+			try {
+				Socket newCli = rfb.accept();
+				rfb.sendInitData(newCli);
+				rfb.addSock(newCli);
+			} catch (IOException e) {
+
+			}
+		}
+	}
+}