changeset 60:18a19d8a09f4

add some files. for to use Client with CUI.
author e085711
date Fri, 05 Aug 2011 01:05:38 +0900
parents d086ec1b4a7a
children 646e6fc14d62
files src/myVncClient/AcceptThread.java src/myVncClient/CuiMyVncClient.java src/myVncClient/CuiVncCanvas.java src/myVncClient/EchoClient.java src/myVncClient/InterfaceForViewer.java src/myVncClient/MyRfbProto.java src/myVncClient/MyVncClient.java src/myVncClient/OptionNoFrame.java src/myVncClient/VncCanvas.java src/myVncClient/VncViewer.java src/myVncClient/WaitReply.java
diffstat 11 files changed, 3654 insertions(+), 195 deletions(-) [+]
line wrap: on
line diff
--- a/src/myVncClient/AcceptThread.java	Fri Jul 29 17:26:17 2011 +0900
+++ b/src/myVncClient/AcceptThread.java	Fri Aug 05 01:05:38 2011 +0900
@@ -4,28 +4,33 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 
-import myVncClient.MyRfbProto;
+public class AcceptThread implements Runnable {
+        MyRfbProto rfb;
+        byte[] imageBytes;
+        int port;
+        
+        AcceptThread(MyRfbProto _rfb) {
+                rfb = _rfb;
+        }
 
-public class AcceptThread implements Runnable {
-    MyRfbProto rfb;
-    byte[] imageBytes;
+        AcceptThread(MyRfbProto _rfb, int p) {
+            rfb = _rfb;
+            port = p;
+        }
 
-    AcceptThread(MyRfbProto _rfb ) {
-	rfb = _rfb;
-    }
-    public void run() {
-	rfb.selectPort();
-	while (true) {
-	    try {
-		Socket newCli = rfb.accept();
-		
-		OutputStream os = newCli.getOutputStream();
-		InputStream is = newCli.getInputStream();
-		rfb.newClient(this, newCli, os, is);
-	    } catch (IOException e) {
-		e.printStackTrace();
-		System.out.println(e);
-	    }
-	}
-    }
-}
\ No newline at end of file
+        public void run() {
+                rfb.selectPort(port);
+                while (true) {
+                        try {
+                                Socket newCli = rfb.accept();
+
+                                OutputStream os = newCli.getOutputStream();
+                                InputStream is = newCli.getInputStream();
+                                rfb.newClient(this, newCli, os, is);
+                        } catch (IOException e) {
+                                e.printStackTrace();
+                                System.out.println(e);
+                        }
+                }
+        }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncClient/CuiMyVncClient.java	Fri Aug 05 01:05:38 2011 +0900
@@ -0,0 +1,881 @@
+package myVncClient;
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.net.*;
+
+import myVncClient.AcceptThread;
+import myVncClient.OptionsNoFrame;
+
+public class CuiMyVncClient implements InterfaceForViewer, java.lang.Runnable {
+	
+	public static void main(String[] argv) {
+		CuiMyVncClient v = new CuiMyVncClient();
+		v.runClient(argv, v);
+		
+	}
+	
+	private void runClient(String[] argv, CuiMyVncClient v) {
+		mainArgs = argv;
+
+		v.init(null);
+		v.start_threads();
+		
+	}
+	
+	
+	
+	String[] mainArgs;
+	String username;
+	
+	// RfbProto rfb;
+	MyRfbProto rfb;
+	Thread rfbThread;
+	Thread accThread;
+	Thread clientThread;
+
+	Frame vncFrame;
+	Container vncContainer;
+	ScrollPane desktopScrollPane;
+	GridBagLayout gridbag;
+	ButtonPanel buttonPanel;
+	Label connStatusLabel;
+	CuiVncCanvas vc;
+//	OptionsFrame options;
+	OptionsNoFrame 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;
+
+	private static final long serialVersionUID = 1L;
+	boolean inAnApplet = true;
+	boolean inSeparateFrame = false;
+	Socket clientSocket = null;
+	String parent, treenum;
+	private String leaderflag;
+	boolean runflag = false;
+
+	EchoClient echo;
+
+	
+	void checkArgs(String[] argv){
+		if(argv.length > 3){
+			username = argv[3]; 
+		}else if(argv.length < 2){
+			System.out.println("Please enter argv");
+			System.out.println("hostname(IPaddress) port password");
+			System.exit(0);
+		}else{
+			username = argv[0];
+		}
+	}
+	
+	//
+	// init()
+	//
+
+	public void init(EchoClient value) {
+		
+		// readParameters();
+
+		readParameters(value);
+
+		options = new OptionsNoFrame(this);
+		recordingSync = new Object();
+
+		sessionFileName = null;
+		recordingActive = false;
+		recordingStatusChanged = false;
+		cursorUpdatesDef = null;
+		eightBitColorsDef = null;
+
+		try{
+			connectAndAuthenticate();
+		}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(Exception e){}
+
+		rfbThread = new Thread(this);
+		accThread = new Thread(new AcceptThread(rfb, 5999)); 
+
+	}
+
+	//
+	// run() - executed by the rfbThread to deal with the RFB socket.
+	//
+
+	public void start_threads(){
+		rfbThread.start();
+	}
+	
+	
+	public void run() {
+
+		try {
+//			connectAndAuthenticate();
+			doProtocolInitialisation();
+/*
+			htmlFile = new CreateHtmlFile(rfb, host, username);
+			htmlFile.createHtml();
+*/
+			vc = new CuiVncCanvas(this, 0, 0);
+			vc.updateFramebufferSize();
+			
+			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);
+		 */
+		showConnectionStatus("ask password...");
+		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(EchoClient value) {
+		/*
+		 * host = readParameter("HOST", !inAnApplet);
+		 * 
+		 * if (host == null) { host = getCodeBase().getHost(); if
+		 * (host.equals("")) { fatalError("HOST parameter not specified"); } }
+		 * 
+		 * port = readIntParameter("PORT", 5550);
+		 */
+		if (value == null) {
+
+			if (clientSocket == null) {
+				String pHost;
+				if (mainArgs.length > 0) pHost = mainArgs[0];
+				else pHost = "cls080.ie.u-ryukyu.ac.jp";
+				echo = new EchoClient(pHost,this);
+				echo.openport();
+				
+				value = echo.hostn("1");
+			} else {
+				echo = new EchoClient();
+				value = echo.Interruption(clientSocket);
+			}
+		}
+		// proxyからの返信で接続先を決定する
+		host = value.responseLine;
+		parent = value.parent;
+		if(value.treenum != null) {
+			treenum = value.treenum;
+		} else {
+			treenum = echo.treenum;
+		}
+		if(value.leaderflag != null) {
+			leaderflag = value.leaderflag;
+		} else {
+			leaderflag = echo.leaderflag;
+		}
+		System.out.println("Parent =" + parent);
+		System.out.println("mynumber =" + treenum);
+		System.out.println("connect host =" + host);
+		System.out.println("leaderflag(boolean) = " + leaderflag);
+		
+		echo = value;
+
+/*
+		if (host == null) {
+			host = getCodeBase().getHost();
+			if (host.equals("")) {
+				fatalError("HOST parameter not specified");
+			}
+		}
+*/
+		port = 5999;
+		// 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) {
+		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) {
+	}
+
+	public void setClientSocket(Socket sock) {
+		clientSocket = sock;
+	}
+
+	
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncClient/CuiVncCanvas.java	Fri Aug 05 01:05:38 2011 +0900
@@ -0,0 +1,1935 @@
+package myVncClient;
+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;
+
+import javax.imageio.ImageIO;
+
+//
+//VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
+//
+
+class CuiVncCanvas extends Canvas implements KeyListener, MouseListener,
+		MouseMotionListener {
+
+	CuiMyVncClient viewer;
+	MyRfbProto rfb;
+	ColorModel cm8, cm24;
+	Color[] colors;
+	int bytesPixel;
+
+	int maxWidth = 0, maxHeight = 0;
+	int scalingFactor;
+	int scaledWidth, scaledHeight;
+
+//	Image memImage;
+	BufferedImage memImage;
+	Graphics memGraphics;
+
+	Image rawPixelsImage;
+//	BufferedImage rawPixelsImage;
+	BufferedImage bimg;
+
+	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;
+	private int b = 0;
+	
+
+	
+	//
+	// The constructors.
+	//
+
+	public CuiVncCanvas(CuiMyVncClient 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 CuiVncCanvas(CuiMyVncClient 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();
+			}
+		}
+*/
+		memImage = new BufferedImage(rfb.framebufferWidth, rfb.framebufferHeight, BufferedImage.TYPE_INT_RGB );
+		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);
+//		rawPixelsImage = (BufferedImage) 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;
+		while (true) {
+			System.out.println("\ncount=" + count);
+			
+			count++;
+			
+			/**
+			 *  read Data from parents and send Data to Client.
+			 */
+			rfb.sendDataToClient();
+			
+			long numBytesRead = rfb.getNumBytesRead();
+			
+			// Read message type from the server. 
+			int msgType = rfb.readServerMessageType();
+
+			// Process the message depending on its type.
+			switch (msgType) {
+			case MyRfbProto.SpeedCheckMillis:
+				rfb.readSpeedCheck();
+				
+				break;
+			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);
+			}
+
+			int bufSize = (int)(rfb.getNumBytesRead() - numBytesRead);
+//			System.out.println("bufSize="+bufSize);
+//			rfb.bufResetSend(bufSize);
+
+
+
+			if(rfb.createBimgFlag){
+//				bimg = createBufferedImage(rawPixelsImage);
+				bimg = createBufferedImage(memImage);
+				//bimg(BufferedImage) -> rfb.pngBytes(byte[])
+				rfb.createPngBytes(bimg);
+				rfb.sendPngImage();	
+				rfb.createBimgFlag = false;
+			}
+
+			
+/*
+			boolean result = false;
+			try{
+				result = ImageIO.write(bimg, "png", new File("sample.png"));
+			}catch(Exception e){
+				e.printStackTrace();
+				result = false;
+			}
+*/
+		}
+	}
+
+	//
+	// 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);
+		}
+	}
+
+	BufferedImage createBufferedImage(Image img){
+		BufferedImage bimg = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB  );
+
+		Graphics g = bimg.getGraphics();
+		g.drawImage(img, 0, 0, null);
+		g.dispose();
+		return bimg;
+	}
+	
+	byte[] getBytes(BufferedImage img)throws IOException { 
+		byte[] b = getImageBytes(img, "raw");
+		return b;
+	}
+	
+	byte[] getImageBytes(BufferedImage image, String imageFormat) throws IOException {
+		ByteArrayOutputStream bos = new ByteArrayOutputStream();
+		BufferedOutputStream os = new BufferedOutputStream(bos);
+		image.flush();
+		ImageIO.write(image, imageFormat, os);
+		os.flush();
+		os.close();
+		return bos.toByteArray();
+	}
+	
+	
+}
--- a/src/myVncClient/EchoClient.java	Fri Jul 29 17:26:17 2011 +0900
+++ b/src/myVncClient/EchoClient.java	Fri Aug 05 01:05:38 2011 +0900
@@ -18,9 +18,8 @@
 	String treenum;// 自分の番号
 	String leaderflag;// リーダフラグ
 	Socket clientSocket = null;
-	MyVncClient client;
-	
-	
+//	MyVncClient client;
+	InterfaceForViewer client;
 
 	// WaitReplyに自分自身を渡している
 	public EchoClient() {
@@ -40,6 +39,20 @@
 		treenum = echo.treenum;
 	}
 
+	public EchoClient(String _name,CuiMyVncClient client) {
+		this.client = client;
+		name = _name;
+	}
+	
+	public EchoClient(EchoClient echo,CuiMyVncClient client) {
+		this.client = client;
+		name = echo.name;
+		leaderflag = echo.leaderflag;
+		parent = echo.parent;
+		treenum = echo.treenum;
+	}
+
+	
 	// void hostn(String args){
 	void openport() {
 		//  ソケットや入出力用のストリームの宣言
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncClient/InterfaceForViewer.java	Fri Aug 05 01:05:38 2011 +0900
@@ -0,0 +1,28 @@
+package myVncClient;
+
+import java.awt.Graphics; 
+import java.net.Socket;
+
+public interface InterfaceForViewer {
+
+	public void init(EchoClient value);
+	public void start_threads();
+	public void start();
+	
+	
+	public String readParameter(String name, boolean required);
+	
+	// synchronized
+	public void disconnect();
+	public void fatalError(String str);
+	public void fatalError(String str, Exception e);
+
+	
+	public void destroy();
+	
+	public void enableInput(boolean enable);
+	
+
+	public void setClientSocket(Socket sock);
+	
+}
--- a/src/myVncClient/MyRfbProto.java	Fri Jul 29 17:26:17 2011 +0900
+++ b/src/myVncClient/MyRfbProto.java	Fri Aug 05 01:05:38 2011 +0900
@@ -13,19 +13,31 @@
 import java.net.BindException;
 import java.net.ServerSocket;
 import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.util.Iterator;
 import java.util.LinkedList;
 
 import javax.imageio.ImageIO;
 
+
 import myVncClient.MulticastQueue.Client;
 
 import java.util.concurrent.ExecutorService;
-//import java.util.concurrent.Executors;
+import java.util.zip.DataFormatException;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
 import java.io.OutputStream;
 
-class MyRfbProto extends RfbProto {
-
+public
+class MyRfbProto<ByteBuffersIterator> extends RfbProto {
 	final static String versionMsg_3_998 = "RFB 003.998\n";
+	/**
+	 * CheckMillis is one of new msgType for RFB 3.998. 
+	 */
+	final static byte SpeedCheckMillis = 4;
+	private static final int INFLATE_BUFSIZE = 1024*100;
+	boolean printStatusFlag = false;
+	long startCheckTime;
 
 	private int messageType;
 	private int rectangles;
@@ -35,20 +47,23 @@
 	private int rectH;
 	private int encoding;
 	private int zLen;
+	private boolean clicomp;
 
 	private ServerSocket servSock;
 	private int acceptPort;
 	private byte initData[];
 	private LinkedList<Socket> cliListTmp;
 	private LinkedList<Socket> cliList;
-	//private LinkedList<Thread> sendThreads;
 	boolean createBimgFlag;
 
 	ExecutorService executor;
 
 	byte[] pngBytes;
 
-	private MulticastQueue<byte[]> multicastqueue = new MulticastQueue<byte[]>();
+	// private MulticastQueue<LinkedList<ByteBuffer>> multicastqueue = new MostRecentMultiCast<LinkedList<ByteBuffer>>(10);
+	private MulticastQueue<LinkedList<ByteBuffer>> multicastqueue = new MulticastQueue<LinkedList<ByteBuffer>>();
+	private int clients = 0;
+	private Inflater inflater = new Inflater();
 
 	MyRfbProto(String h, int p, VncViewer v) throws IOException {
 		super(h, p, v);
@@ -98,21 +113,21 @@
 		acceptPort = port;
 	}
 
-	// 5550を開けるが、開いてないなら+1のポートを開ける。
-	void selectPort() {
-		int i = 5550;
+
+	void selectPort(int p) {
+		int port = p;
 		while (true) {
 			try {
-				initServSock(i);
+				initServSock(port);
 				break;
 			} catch (BindException e) {
-				i++;
+				port++;
 				continue;
 			} catch (IOException e) {
 
 			}
 		}
-		System.out.println("accept port = " + i);
+		System.out.println("accept port = " + port);
 	}
 
 	int getAcceptPort() {
@@ -136,30 +151,22 @@
 		cliListTmp.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);
+		is.mark(255);
 		skipBytes(20);
 		int nlen = readU32();
 		int blen = 20 + 4 + nlen;
 		initData = new byte[blen];
-		reset();
+		is.reset();
 
-		mark(blen);
+		is.mark(blen);
 		readFully(initData);
-		reset();
+		is.reset();
 
 		framebufferWidth = readU16();
 		framebufferHeight = readU16();
@@ -251,20 +258,6 @@
 		os.write(initData);
 	}
 
-	void sendData(byte b[]) {
-		try {
-			multicastqueue.put(b);
-
-			/*
-			 * // for(Socket cli : cliList){ // try{ //
-			 * cli.getOutputStream().write(b, 0, b.length); //
-			 * }catch(IOException e){ // // if socket closed //
-			 * cliList.remove(cli); // } // }
-			 */
-			// System.out.println("cliSize="+cliSize());
-		} catch (Exception e) {
-		}
-	}
 
 	void sendPngImage() {
 		try {
@@ -296,39 +289,41 @@
 		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(20);
-		messageType = readU8();
-		skipBytes(1);
-		rectangles = readU16();
-		rectX = readU16();
-		rectY = readU16();
-		rectW = readU16();
-		rectH = readU16();
-		encoding = readU32();
-		if (encoding == 16)
+		is.mark(20);
+		messageType = readU8();   // 0
+		skipBytes(1);                    // 1
+		rectangles = readU16();     //  2
+		rectX = readU16();            //  4  
+		rectY = readU16();            //  6
+		rectW = readU16();           //  8
+		rectH = readU16();           //  10
+		encoding = readU32();      //   12
+		System.out.println("encoding = "+encoding);
+		if (encoding == EncodingZRLE)
 			zLen = readU32();
-		reset();
-	}
-
-	int checkAndMark() throws IOException {
+		else
+			zLen = 0;
+		is.reset();
+/*
 		int dataLen;
 		switch (encoding) {
 		case RfbProto.EncodingRaw:
 			dataLen = rectW * rectH * 4 + 16;
 			mark(dataLen);
 			break;
+		case RfbProto.EncodingCopyRect:
+			dataLen = 16 + 4;
+			mark(dataLen);
+			break;
+		case RfbProto.EncodingRRE:
+		case RfbProto.EncodingCoRRE:
+		case RfbProto.EncodingHextile:
+			
+		case RfbProto.EncodingZlib:
+		case RfbProto.EncodingTight:
 		case RfbProto.EncodingZRLE:
 			dataLen = zLen + 20;
 			mark(dataLen);
@@ -337,29 +332,51 @@
 			dataLen = 1000000;
 			mark(dataLen);
 		}
+	
+*/	
+	
+	}
+
+	int checkAndMark() throws IOException {
+		int dataLen;
+		switch (encoding) {
+		case RfbProto.EncodingRaw:
+			dataLen = rectW * rectH * 4 + 16;
+			is.mark(dataLen);
+			break;
+		case RfbProto.EncodingCopyRect:
+			dataLen = 16 + 4;
+			is.mark(dataLen);
+			break;
+		case RfbProto.EncodingRRE:
+		case RfbProto.EncodingCoRRE:
+		case RfbProto.EncodingHextile:
+		case RfbProto.EncodingTight:
+			dataLen = zLen + 20;
+			is.mark(dataLen);
+			break;
+		case RfbProto.EncodingZlib:
+		case RfbProto.EncodingZRLE:
+			dataLen = zLen + 20;
+			is.mark(dataLen);
+			break;
+		case RfbProto.EncodingXCursor:
+		case RfbProto.EncodingRichCursor:
+			int pixArray = rectW * rectH * 4;
+			int u8Array = (int)Math.floor((rectW + 7)/8) * rectH; 
+			dataLen = pixArray + u8Array;
+			printFramebufferUpdate();
+			is.mark(dataLen);
+			break;
+		default:
+			dataLen = 1000000;
+			is.mark(dataLen);
+		}
 		return dataLen;
 	}
-	void readSendData(int dataLen) throws IOException {
-		byte buffer[] = new byte[dataLen];
-		readFully(buffer);
-		multicastqueue.put(buffer);
-		reset();
-/*
-		for (Socket cli : cliList) {
-			try {
-				OutputStream out = cli.getOutputStream();
-				executor.execute(new SendThread(out, buffer));
-			} catch (IOException e) {
-				// if client socket closed
-				cliListTmp.remove(cli);
-			} catch (Exception e) {
+	
 
-			}
-
-		}
-*/
-	}
-	void sendDataToClient() throws IOException {
+	void sendDataToClient() throws Exception {
 		regiFramebufferUpdate();
 		int dataLen = checkAndMark();
 		readSendData(dataLen);		
@@ -414,17 +431,19 @@
 		BufferedImage bimg = ImageIO.read(new ByteArrayInputStream(pngBytes));
 		return bimg;
 	}
-
+/*
 	void readPngData() throws IOException {
 		pngBytes = new byte[is.available()];
 		readFully(pngBytes);
 	}
-
+*/
 	void printFramebufferUpdate() {
 
 		System.out.println("messageType=" + messageType);
 		System.out.println("rectangles=" + rectangles);
 		System.out.println("encoding=" + encoding);
+		System.out.println("rectX = "+rectX+": rectY = "+rectY);
+		System.out.println("rectW = "+rectW+": rectH = "+rectH);
 		switch (encoding) {
 		case RfbProto.EncodingRaw:
 			System.out.println("rectW * rectH * 4 + 16 =" + rectW * rectH * 4
@@ -433,17 +452,223 @@
 		default:
 		}
 	}
+	
+	void readSpeedCheck() throws IOException {
+		byte[] b = new byte[1];
+		readFully(b);
+	}
+	
+	void startSpeedCheck() {
+		ByteBuffer b = ByteBuffer.allocate(10);
+		b.put((byte)SpeedCheckMillis);
+		b.flip();
+		startCheckTime = System.currentTimeMillis();
+		System.out.println("startChckTime = "+ startCheckTime);
+		LinkedList<ByteBuffer>bufs = new LinkedList<ByteBuffer>();
+		bufs.add(b);
+		multicastqueue.put(bufs);
+	}
+
+	void endSpeedCheck() {
+		long accTime = System.currentTimeMillis();
+		long time = accTime - startCheckTime;
+		System.out.println("checkMillis: " + time);
+	}
+
+	void printStatus() {
+		System.out.println();
+	}
+
+	synchronized void changeStatusFlag() {
+		printStatusFlag = true;
+	}
+
+	void printMills() {
+		if(printStatusFlag) {
+
+			changeStatusFlag();
+		} else {
+			changeStatusFlag();
+		}
+	}
+	
+	void speedCheckMillis() {
+			Runnable stdin = new Runnable() {
+			public void run() {
+				int c;
+				try {
+					while( (c = System.in.read()) != -1 ) {
+						switch(c) {
+							case 's':
+								break;
+							default:
+								startSpeedCheck();
+								break;
+						}
+					}
+				}catch(IOException e){
+					System.out.println(e);
+				}
+			}
+		};
+		
+		new Thread(stdin).start();
+	}
+
+	/**
+	 * gzip byte arrays
+	 * @param deflater
+	 * @param inputs
+	 *            byte data[]
+	 * @param inputIndex 
+	 * @param outputs
+	 *            byte data[]
+	 * @return  byte length in last byte array
+	 * @throws IOException
+	 */
+	public int zip(Deflater deflater,LinkedList<ByteBuffer> inputs, int inputIndex, LinkedList<ByteBuffer> outputs) throws IOException {
+		int len = 0;
+		ByteBuffer c1= ByteBuffer.allocate(INFLATE_BUFSIZE);
+		while(inputIndex < inputs.size() ) {
+			ByteBuffer b1 = inputs.get(inputIndex++);
+			deflater.setInput(b1.array(),b1.position(),b1.remaining());
+			if (inputIndex==inputs.size())	deflater.finish();
+			do {
+				int len1 = deflater.deflate(c1.array(),c1.position(),c1.remaining());
+				if (len1>0) {
+					len += len1;
+					c1.position(c1.position()+len1); 
+					if (c1.remaining()==0) {
+						c1.flip();	outputs.addLast(c1);
+						c1 = ByteBuffer.allocate(INFLATE_BUFSIZE);
+					}
+				}
+			} while (!deflater.needsInput()&&!deflater.finished());
+		}
+		if (c1.position()!=0) {
+			c1.flip();	outputs.addLast(c1);
+		}
+		deflater.reset();
+		return len;
+	}
+	
+	/**
+	 * gunzip byte arrays
+	 * @param inflater
+	 * @param inputs
+	 *            byte data[]
+	 * @param outputs
+	 *            byte data[]
+	 *@return  number of total bytes            
+	 * @throws IOException
+	 */
+	public int unzip(Inflater inflater, LinkedList<ByteBuffer> inputs, int inputIndex, LinkedList<ByteBuffer> outputs)
+																	throws DataFormatException {
+		int len=0;
+		ByteBuffer buf = ByteBuffer.allocate(INFLATE_BUFSIZE);
+		while (inputIndex < inputs.size()) {
+			ByteBuffer input = inputs.get(inputIndex++);
+			inflater.setInput(input.array(),input.position(),input.limit());
+			do {
+				int len0 = inflater.inflate(buf.array(),buf.position(),buf.remaining());
+				if (len0>0) {
+					buf.position(buf.position()+len0);
+					len += len0;
+					if (buf.remaining()==0) {
+						buf.flip();
+						outputs.addLast(buf);
+						buf = ByteBuffer.allocate(INFLATE_BUFSIZE);
+					}
+				}
+			} while (!inflater.needsInput());
+		} 
+		if (buf.position()!=0) {
+			buf.flip();
+			outputs.addLast(buf);
+		}
+		return len;
+	}
+	
+	void readSendData(int dataLen) throws IOException, DataFormatException {
+		LinkedList<ByteBuffer>bufs = new LinkedList<ByteBuffer>();
+		ByteBuffer header = ByteBuffer.allocate(16);
+		readFully(header.array(),0,16); 
+		header.limit(16);
+		if (header.get(0)==RfbProto.FramebufferUpdate) {
+			int encoding = header.getInt(12);
+			if (encoding==RfbProto.EncodingZlib||encoding==RfbProto.EncodingZRLE) {
+				ByteBuffer len = ByteBuffer.allocate(4);
+				readFully(len.array(),0,4); len.limit(4);
+				ByteBuffer inputData = ByteBuffer.allocate(dataLen-20);
+				readFully(inputData.array(),0,inputData.capacity()); inputData.limit(dataLen-20);
+				LinkedList<ByteBuffer>inputs = new LinkedList<ByteBuffer>();
+				inputs.add(inputData);
+				if (clicomp) {
+					unzip(inflater, inputs, 0, bufs);
+				} else {
+					Deflater nDeflater = new Deflater();
+					LinkedList<ByteBuffer> out = new LinkedList<ByteBuffer>();
+					unzip(inflater, inputs, 0 , out);
+					int len2 = zip(nDeflater, out, 0, bufs);
+					ByteBuffer blen = ByteBuffer.allocate(4); blen.putInt(len2); blen.flip();
+					bufs.addFirst(blen);
+				}
+				bufs.addFirst(header);
+				multicastqueue.put(bufs);
+				is.reset();
+				return ;
+			}
+		} 
+		bufs.add(header);
+		if (dataLen>16) {
+			ByteBuffer b = ByteBuffer.allocate(dataLen-16);
+			readFully(b.array(),0,dataLen-16); b.limit(dataLen-16);
+			bufs.add(b);
+		}
+		multicastqueue.put(bufs);
+		is.reset();
+
+		// It may be compressed. We can inflate here to avoid repeating clients decompressing here,
+		// but it may generate too many large data. It is better to do it in each client.
+		// But we have do inflation for all input data, so we have to do it here.
+	}
 
 	void newClient(AcceptThread acceptThread, final Socket newCli,
 			final OutputStream os, final InputStream is) throws IOException {
 		// createBimgFlag = true;
 		// rfb.addSockTmp(newCli);
 		//		addSock(newCli);
-		final Client<byte[]> c = multicastqueue.newClient();
-		Runnable sender = new Runnable() {
+		final Client <LinkedList<ByteBuffer>> c = multicastqueue.newClient();
+
+		final Runnable reader = new Runnable() {
 			public void run() {
+				byte b[] = new byte[4096];
+				for(;;) {
+					try {
+						int c = is.read(b);
+						if (c<=0) throw new IOException();
+						System.out.println("client read "+c);
+					} catch (IOException e) {
+						try {
+							os.close();
+							is.close();
+						} catch (IOException e1) {
+						}
+						return;
+					}
+				}
+			}
+		};
+		Runnable sender = new Runnable() {
+
+
+			public void run() {
+
+			    Deflater deflater = new Deflater();
 				try {
-					// 初期接続確立の部分
+					/**
+					 *  initial connection of RFB protocol
+					 */
 					sendRfbVersion(os);
 					readVersionMsg(is);
 					sendSecurityType(os);
@@ -451,23 +676,57 @@
 					sendSecResult(os);
 					readClientInit(is);
 					sendInitData(os);
-
+					new Thread(reader).start();
 					for (;;) {
-						byte[] b = c.poll();
-						os.write(b, 0, b.length);
+						LinkedList<ByteBuffer> bufs = c.poll();
+						int inputIndex = 0;
+						ByteBuffer header = bufs.get(inputIndex++);
+						if (header==null) continue;
+						if (header.get(0)==RfbProto.FramebufferUpdate) {
+							System.out.println("client "+ clients);
+							int encoding = header.getInt(12);
+							if (encoding==RfbProto.EncodingZlib||encoding==RfbProto.EncodingZRLE) {
+								LinkedList<ByteBuffer> outs;
+								if (clicomp) {
+									outs = new LinkedList<ByteBuffer>();
+									int len2 = zip(deflater, bufs, inputIndex, outs);
+									ByteBuffer blen = ByteBuffer.allocate(4); blen.putInt(len2); blen.flip();
+									outs.addFirst(blen);
+									outs.addFirst(header);
+									inputIndex = 0;
+								} else {
+									outs = bufs;
+									inputIndex = 0;
+								}
+								while(inputIndex < outs.size()) {
+								   ByteBuffer out=  outs.get(inputIndex++);
+								   os.write(out.array(),out.position(),out.limit());
+								}
+							}
+							os.flush();
+							continue;
+						}
+						os.write(header.array(),header.position(),header.limit());
+						while(inputIndex < bufs.size()) {
+							ByteBuffer b = bufs.get(inputIndex++);
+							os.write(b.array(), b.position(), b.limit());
+						}
+						os.flush();
 					}
 				} catch (IOException e) {
-					/**
-					 * if socket closed
-					 */
-					//					cliList.remove(newCli);
-
+					try {
+						os.close();
+					} catch (IOException e1) {
+					}
+					/* if socket closed 	cliList.remove(newCli); */
 				}
-
 			}
-
 		};
+		clients++;
 		new Thread(sender).start();
 
 	}
+
 }
+
+
--- a/src/myVncClient/MyVncClient.java	Fri Jul 29 17:26:17 2011 +0900
+++ b/src/myVncClient/MyVncClient.java	Fri Aug 05 01:05:38 2011 +0900
@@ -6,8 +6,7 @@
 import java.net.*;
 import java.util.Random;
 
-
-public class MyVncClient extends VncViewer implements java.lang.Runnable,
+public class MyVncClient extends VncViewer implements InterfaceForViewer, java.lang.Runnable,
 		WindowListener {
 
 	/**
@@ -21,14 +20,12 @@
 	private String leaderflag;
 	boolean runflag = 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){
+	public static void main(String[] argv) {
 		MyVncClient v = new MyVncClient();
 		v.runClient(argv);
 	}
@@ -48,7 +45,7 @@
 	//
 
 	public void init(EchoClient value) {
-		
+
 		// readParameters();
 		readParameters(value);
 
@@ -90,11 +87,11 @@
 	// run() - executed by the rfbThread to deal with the RFB socket.
 	//
 
-	public void start_threads(){
+	public void start_threads() {
 		rfbThread.start();
 	}
-	
-	public void run() { 
+
+	public void run() {
 		gridbag = new GridBagLayout();
 		vncContainer.setLayout(gridbag);
 
@@ -107,11 +104,11 @@
 			gridbag.setConstraints(buttonPanel, gbc);
 			vncContainer.add(buttonPanel);
 		}
-		
+
 		try {
 
 			connectAndAuthenticate();
-			accThread = new Thread(new AcceptThread(rfb));
+			accThread = new Thread(new AcceptThread(rfb, 5999));
 			accThread.start();
 
 			doProtocolInitialisation();
@@ -127,42 +124,41 @@
 			Random rnd = new Random();
 			long ran = rnd.nextInt(5000) + 5000;
 			System.out.println(ran);
-			//親がいない場合の処理はここに書く!!!!
+			// 親がいない場合の処理はここに書く!!!!
 			/**
-			 * this while reconnection  
+			 * this while reconnection
 			 */
-			
+
 			int counter = 0;
-			//window を消してnullを突っ込んでGCで削除させる。
+			// window を消してnullを突っ込んでGCで削除させる。
 			/*
-			vncFrame.setVisible(false);
-			vncFrame = null;
-			*/
+			 * vncFrame.setVisible(false); vncFrame = null;
+			 */
 			while (true) {
 				/**
-				 * if my last node case reconnectoion stop    
+				 * if my last node case reconnectoion stop
 				 */
-						
-				echo = new EchoClient(echo,this);
+
+				echo = new EchoClient(echo, this);
 				try {
 					Thread.sleep(ran);
-				} catch (InterruptedException e1) {				
+				} catch (InterruptedException e1) {
 					e1.printStackTrace();
 				}
-				
-				if(counter >= 3) {
+
+				if (counter >= 3) {
 					echo.openport();
 					echo.notfoundParent();
 				}
-		
+
 				echo.openport();
-				//runflag = echo.losthost();
-				if(echo.losthost()) {
+				// runflag = echo.losthost();
+				if (echo.losthost()) {
 					break;
 				}
 				counter++;
 			}
-			
+
 			// System.exit(0);
 		} catch (Exception e) {
 			System.out.println(e);
@@ -219,17 +215,17 @@
 					+ ":" + port, e);
 		} catch (EOFException e) {
 
-			//window を消してnullを突っ込んでGCで削除させる。
+			// window を消してnullを突っ込んでGCで削除させる。
 			vncFrame.setVisible(false);
 			vncFrame = null;
-			//num4
+			// num4
 			// リーダーの子ノードがproxyに対して親が落ちたことを報告をする
-			if(leaderflag != null) {
-				while(true) {
-					echo = new EchoClient(echo,this);
+			if (leaderflag != null) {
+				while (true) {
+					echo = new EchoClient(echo, this);
 					echo.openport();
-					//runflag = echo.losthost();
-					if(echo.losthost()) {
+					// runflag = echo.losthost();
+					if (echo.losthost()) {
 						break;
 					}
 				}
@@ -282,7 +278,6 @@
 	// 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.
@@ -694,11 +689,11 @@
 	}
 
 	/**
-	 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.
-	*/
+	 * 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(EchoClient value) {
 		/*
@@ -713,11 +708,13 @@
 
 			if (clientSocket == null) {
 				String pHost;
-				if (mainArgs.length > 0) pHost = mainArgs[0];
-				else pHost = "cls080.ie.u-ryukyu.ac.jp";
-				echo = new EchoClient(pHost,this);
+				if (mainArgs.length > 0)
+					pHost = mainArgs[0];
+				else
+					pHost = "cls080.ie.u-ryukyu.ac.jp";
+				echo = new EchoClient(pHost, this);
 				echo.openport();
-				
+
 				value = echo.hostn("1");
 			} else {
 				echo = new EchoClient();
@@ -727,12 +724,12 @@
 		// proxyからの返信で接続先を決定する
 		host = value.responseLine;
 		parent = value.parent;
-		if(value.treenum != null) {
+		if (value.treenum != null) {
 			treenum = value.treenum;
 		} else {
 			treenum = echo.treenum;
 		}
-		if(value.leaderflag != null) {
+		if (value.leaderflag != null) {
 			leaderflag = value.leaderflag;
 		} else {
 			leaderflag = echo.leaderflag;
@@ -741,7 +738,7 @@
 		System.out.println("mynumber =" + treenum);
 		System.out.println("connect host =" + host);
 		System.out.println("leaderflag(boolean) = " + leaderflag);
-		
+
 		echo = value;
 
 		if (host == null) {
@@ -750,7 +747,7 @@
 				fatalError("HOST parameter not specified");
 			}
 		}
-		port = 5550;
+		port = 5999;
 		// Read "ENCPASSWORD" or "PASSWORD" parameter if specified.
 		readPasswordParameters();
 
@@ -832,20 +829,12 @@
 			}
 			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;
-				}
-			}
-		}
-*/
+		/*
+		 * 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");
 		}
@@ -962,7 +951,7 @@
 			System.exit(1);
 		}
 	}
-
+	
 	//
 	// Show message text and optionally "Relogin" and "Close" buttons.
 	//
@@ -1076,7 +1065,7 @@
 
 	public static void main(Socket _clientSocket) {
 		MyVncClient v = new MyVncClient();
-		//v.clientSocket = _clientSocket;
+		// v.clientSocket = _clientSocket;
 		// v.mainArgs = argv;
 		v.inAnApplet = false;
 		v.inSeparateFrame = true;
@@ -1092,8 +1081,14 @@
 		v.inSeparateFrame = true;
 
 		v.init(value);
-		v.start_threads();		
+		v.start_threads();
 		v.start();
 
 	}
+	
+	public void setClientSocket(Socket sock) {
+		clientSocket = sock;
+	}
+
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncClient/OptionNoFrame.java	Fri Aug 05 01:05:38 2011 +0900
@@ -0,0 +1,330 @@
+package myVncClient;
+
+import java.awt.*;
+import java.awt.event.*;
+
+class OptionsNoFrame{
+
+  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;
+  CuiMyVncClient 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.
+  //
+
+  OptionsNoFrame(CuiMyVncClient v) {
+    viewer = v;
+    
+    for (int i = 0; i < names.length; i++) {
+    	labels[i] = new Label(names[i]);
+    	
+    	choices[i] = new Choice();
+    	
+
+        for (int j = 0; j < values[i]. length; j++) {
+        	choices[i].addItem(values[i][j]);
+        }
+    }
+    
+    // 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();
+
+    }
+  }
+
+}
--- a/src/myVncClient/VncCanvas.java	Fri Jul 29 17:26:17 2011 +0900
+++ b/src/myVncClient/VncCanvas.java	Fri Aug 05 01:05:38 2011 +0900
@@ -395,7 +395,7 @@
 			 *  read Data from parents and send Data to Client.
 			 *  
 			 */
-			rfb.sendDataToClient();			
+			rfb.sendDataToClient();
 
 			int bufSize = (int)rfb.getNumBytesRead();
 			
@@ -424,6 +424,9 @@
 				for (int i = 0; i < rfb.updateNRects; i++) {
 
 					rfb.readFramebufferUpdateRectHdr();
+					System.out.println("encoding = "+rfb.updateRectEncoding);
+
+					
 					statNumTotalRects++;
 					int rx = rfb.updateRectX, ry = rfb.updateRectY;
 					int rw = rfb.updateRectW, rh = rfb.updateRectH;
--- a/src/myVncClient/VncViewer.java	Fri Jul 29 17:26:17 2011 +0900
+++ b/src/myVncClient/VncViewer.java	Fri Aug 05 01:05:38 2011 +0900
@@ -5,7 +5,7 @@
 import java.io.*;
 import java.net.*;
 
-public class VncViewer extends java.applet.Applet implements
+public class VncViewer extends java.applet.Applet implements 
 		java.lang.Runnable, WindowListener {
 
 	boolean inAnApplet = true;
@@ -147,7 +147,7 @@
 			
 			rfb = new MyRfbProto(host, port, this);
 			rfb.readServerInit();
-			rfb.readPngData();
+//			rfb.readPngData();
 			
 			createCanvas(0, 0);
 			vc.drawFirstImage();
@@ -676,8 +676,7 @@
 
 		if (host == null) { host = getCodeBase().getHost(); 
 			if (host.equals("")) {
-				host = "nw0811.st.ie.u-ryukyu.ac.jp";
-//				fatalError("HOST parameter not specified"); 	
+				fatalError("HOST parameter not specified"); 	
 			} 
 		}
 
--- a/src/myVncClient/WaitReply.java	Fri Jul 29 17:26:17 2011 +0900
+++ b/src/myVncClient/WaitReply.java	Fri Aug 05 01:05:38 2011 +0900
@@ -5,14 +5,24 @@
 
 
 public class WaitReply extends Thread {
-	MyVncClient client;
+//	MyVncClient client;
+	InterfaceForViewer client;
 	private String treenum;
 
+
+/*	
 	public WaitReply(String treenum,MyVncClient client) {
 		this.client = client;
 		this.treenum = treenum;
 	}
+*/
 
+	public WaitReply(String treenum,InterfaceForViewer client) {
+		this.client = client;
+		this.treenum = treenum;
+	}
+
+	
 	public void run(){
 		Socket clientSocket = null;
 		ServerSocket echoServer=null;
@@ -24,11 +34,12 @@
 			}
 			catch (IOException e) {
 				System.out.println(e);
-			}                                                                                                                
+			}
 			try {
 			clientSocket = echoServer.accept();
 			if(clientSocket != null){
-				client.clientSocket = clientSocket;
+//				client.clientSocket = clientSocket;
+				client.setClientSocket(clientSocket);
 				client.init(null);
 				client.start_threads();
 				client.start();