changeset 116:5b124b0ceaa7

merge
author Yu Taninari <e085734@ie.u-ryukyu.ac.jp>
date Sat, 06 Aug 2011 23:57:18 +0900
parents 7d9e9dfd7eb8 (current diff) 02016fcb9105 (diff)
children 65cb34c3f7ab 32dbcb891835
files src/myVncProxy/CreateThread.java
diffstat 11 files changed, 960 insertions(+), 163 deletions(-) [+]
line wrap: on
line diff
--- a/.classpath	Sat Aug 06 23:56:16 2011 +0900
+++ b/.classpath	Sat Aug 06 23:57:18 2011 +0900
@@ -2,5 +2,6 @@
 <classpath>
 	<classpathentry kind="src" path="src"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
--- a/src/myVncProxy/AcceptThread.java	Sat Aug 06 23:56:16 2011 +0900
+++ b/src/myVncProxy/AcceptThread.java	Sat Aug 06 23:57:18 2011 +0900
@@ -1,31 +1,36 @@
 package myVncProxy;
-import java.net.ServerSocket;
 import java.net.Socket;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
 public class AcceptThread implements Runnable {
-	MyRfbProto rfb;
-	byte[] imageBytes;
+        MyRfbProto rfb;
+        byte[] imageBytes;
+        int port;
+        
+        AcceptThread(MyRfbProto _rfb) {
+                rfb = _rfb;
+        }
+
+        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);
-			}
-		}
-	}
+        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);
+                        }
+                }
+        }
 }
-
--- a/src/myVncProxy/CreateThread.java	Sat Aug 06 23:56:16 2011 +0900
+++ b/src/myVncProxy/CreateThread.java	Sat Aug 06 23:57:18 2011 +0900
@@ -22,20 +22,13 @@
 	public void run() {
 		
 		while (true) {
-			
 			try {
 				echoServer = new ServerSocket(9999);
-			}
-			catch (IOException e) {
-				System.out.println(e);
-			}
-			try {
 				Socket clientSocket = echoServer.accept();
 				BufferedReader is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
 				PrintStream os = new PrintStream(clientSocket.getOutputStream());
 				acceptClient.transferParentAddrerss(is,os);
 			} catch (IOException e){
-				e.printStackTrace();
 				System.out.println(e);
 			}
 			try {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/MostRecentMultiCast.java	Sat Aug 06 23:57:18 2011 +0900
@@ -0,0 +1,27 @@
+package myVncProxy;
+
+import java.util.LinkedList;
+
+
+public class MostRecentMultiCast<T> extends MulticastQueue<T> {
+
+	LinkedList<Node<T>> alive;
+	int count = 0;
+	MostRecentMultiCast(int limit) {
+		count = limit;
+		this.alive = new LinkedList<Node<T>>();
+	}
+
+	@Override
+	public synchronized void put(T item)
+	{
+		Node<T> next = new Node<T>(item);
+		tail.set(next);
+		tail = next;
+		alive.addLast(next);
+		if (alive.size()>count) {
+			Node<T> old = alive.getFirst();
+			old.clear();
+		}
+	}
+}
--- a/src/myVncProxy/MulticastQueue.java	Sat Aug 06 23:56:16 2011 +0900
+++ b/src/myVncProxy/MulticastQueue.java	Sat Aug 06 23:57:18 2011 +0900
@@ -33,21 +33,24 @@
 			node = tail;
 		}
 		
-		public T poll()
+		synchronized public T poll()
 		{
 			Node<T> next = null;
-			
-			try {
-				next = node.next();
-			}catch(InterruptedException _e){
-				_e.printStackTrace();
-			}
-			node = next;
-			return next.item;
+			T item = null;
+			do {
+				try {
+					next = node.next();
+				}catch(InterruptedException _e){
+					continue;
+				}
+				item = node.getItem();
+				node = next;
+			} while ( item == null);
+			return item;
 		}
 	}
 	
-	private static class Node<T>
+	static class Node<T>
 	{
 		private T item;
 		private Node<T> next;
@@ -60,6 +63,10 @@
 			latch = new CountDownLatch(1);
 		}
 		
+		synchronized public T getItem() {
+			return item;
+		}
+
 		public void set(Node<T> next)
 		{
 			this.next = next;
@@ -71,5 +78,9 @@
 			latch.await();
 			return next;
 		}
+
+		synchronized public void clear() {
+			item = null;
+		}
 	}
 }
--- a/src/myVncProxy/MyRfbProto.java	Sat Aug 06 23:56:16 2011 +0900
+++ b/src/myVncProxy/MyRfbProto.java	Sat Aug 06 23:57:18 2011 +0900
@@ -1,5 +1,7 @@
 package myVncProxy;
 
+import static org.junit.Assert.*;
+
 import java.awt.Graphics;
 import java.awt.Image;
 import java.awt.image.BufferedImage;
@@ -13,19 +15,34 @@
 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 org.junit.Test;
+
 import myVncProxy.MulticastQueue.Client;
 
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.zip.DataFormatException;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
 import java.io.OutputStream;
 
+public
 class MyRfbProto 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,21 +52,29 @@
 	private int rectH;
 	private int encoding;
 	private int zLen;
+	private boolean clicomp = false;
 
 	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();
+	private Deflater deflater = new Deflater();
 
+	public
+	MyRfbProto() throws IOException {
+	}
+	
 	MyRfbProto(String h, int p, VncViewer v) throws IOException {
 		super(h, p, v);
 		cliList = new LinkedList<Socket>();
@@ -99,20 +124,20 @@
 	}
 
 	// 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 +161,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 +268,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,30 +299,25 @@
 		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|| encoding==EncodingZRLEE||encoding==EncodingZlib)
 			zLen = readU32();
-		reset();
+		else
+			zLen = 0;
+		is.reset();
+
 	}
 
 	int checkAndMark() throws IOException {
@@ -327,39 +325,42 @@
 		switch (encoding) {
 		case RfbProto.EncodingRaw:
 			dataLen = rectW * rectH * 4 + 16;
-			mark(dataLen);
+			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:
+		case RfbProto.EncodingZRLEE:
 			dataLen = zLen + 20;
-			mark(dataLen);
+			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;
-			mark(dataLen);
+			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);		
@@ -415,16 +416,13 @@
 		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 +431,285 @@
 		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);
+	}
+
+
+	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 we finish() stream and reset() it, Deflater start new gzip stream, this makes continuous zlib reader unhappy.
+			 * if we remove finish(), Deflater.deflate() never flushes its output. The original zlib deflate has flush flag. I'm pretty
+			 * sure this a kind of bug of Java library.
+			 */
+			if (inputIndex==inputs.size())	
+				deflater.finish();
+			int len1 = 0;
+			do {
+				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 (len1 >0 || !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,int bufSize)
+																	throws DataFormatException {
+		int len=0;
+		ByteBuffer buf = ByteBuffer.allocate(bufSize);
+		while (inputIndex < inputs.size()) {
+			ByteBuffer input = inputs.get(inputIndex++);
+			inflater.setInput(input.array(),input.position(),input.limit());
+//			if (inputIndex==inputs.size())	    if inflater/deflater has symmetry, we need this
+//				inflater.end();                         but this won't work
+			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(bufSize);
+					}
+				}
+			} while (!inflater.needsInput());
+		} 
+		if (buf.position()!=0) {
+			buf.flip();
+			outputs.addLast(buf);
+		}
+		return len;
+	}
+	
+	/**
+	 * send data to clients
+	 * @param dataLen
+	 * @throws IOException
+	 * @throws DataFormatException
+	 * 
+	 * Zlibed packet is compressed in context dependent way, that is, it have to send from the beginning. But this is
+	 * impossible. So we have to compress it again for each clients. Separate deflater for each clients is necessary. 
+	 * 
+	 * Java's deflater does not support flush. This means to get the result, we have to finish the compression. Reseting
+	 * start new compression, but it is not accepted well in zlib continuous reading. So we need new Encoding ZRLEE
+	 * which reset decoder for each packet. ZRLEE can be invisible from user, but it have to be implemented in the clients.
+	 * ZRLEE compression is not context dependent, so no recompression is necessary.
+	 */
+	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.EncodingZRLE||encoding==RfbProto.EncodingZlib) { // ZRLEE is already recompressed
+				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);
+
+				header.putInt(12, RfbProto.EncodingZRLEE); // means recompress every time
+				// using new Deflecter every time is incompatible with the protocol, clients have to be modified.
+				Deflater nDeflater = deflater; // new Deflater();
+				LinkedList<ByteBuffer> out = new LinkedList<ByteBuffer>();
+				unzip(inflater, inputs, 0 , out, INFLATE_BUFSIZE);
+				// dump32(inputs);
+				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();
+		final int myId = clients; 
+		final Client <LinkedList<ByteBuffer>> c = multicastqueue.newClient();
+		final AtomicInteger writerRunning = new AtomicInteger();
+		writerRunning.set(1);
+		/**
+		 * Timeout thread. If a client is suspended, it has top of queue indefinitely, which caused memory
+		 * overflow. After the timeout, we poll the queue and discard it. Start long wait if writer is running.
+		 */
+		final Runnable timer = new Runnable() {
+			public void run() {
+				int count = 0;
+				for(;;) {
+					long timeout = 30000/8;
+					try {
+						synchronized(this) {
+							int state,flag;
+							writerRunning.set(0);
+							wait(timeout);
+							flag = 0;
+							while((state=writerRunning.get())==0) {
+								c.poll(); // discard, should be timeout
+								count++;
+								if (flag==0) {
+									System.out.println("Discarding "+myId + " count="+ count); flag = 1;
+								}
+								wait(10); // if this is too short, writer cannot take the poll, if this is too long, memory will overflow...
+							}
+							if (flag==1) System.out.println("Resuming "+myId + " count="+count);
+							if (state!=1) {
+								System.out.println("Client died "+myId);
+								break;
+							}
+						}
+					} catch (InterruptedException e) {
+					}
+				}
+			}
+		};
+		new Thread(timer).start();
+		/**
+		 * discard all incoming from clients
+		 */
+		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 {
+							writerRunning.set(2);
+							os.close();
+							is.close();
+						} catch (IOException e1) {
+						}
+						return;
+					}
+				}
+			}
+		};
+		/**
+		 * send packets to a client
+		 */
 		Runnable sender = new Runnable() {
 			public void run() {
+				writerRunning.set(1);
 				try {
-					// 初期接続確立の部分
+					/**
+					 *  initial connection of RFB protocol
+					 */
 					sendRfbVersion(os);
 					readVersionMsg(is);
 					sendSecurityType(os);
@@ -451,21 +717,170 @@
 					sendSecResult(os);
 					readClientInit(is);
 					sendInitData(os);
-
+					new Thread(reader).start(); // discard incoming packet here after.
 					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 "+ myId);
+						}
+						writeToClient(os, bufs, inputIndex);
+						writerRunning.set(1);  // yes my client is awaking.
 					}
 				} catch (IOException e) {
-					//接続が切れた処理
-					//lockしないと駄目
-					//					cliList.remove(newCli);
+					try {
+						writerRunning.set(2);
+						os.close();
+					} catch (IOException e1) {
+					}
+					/* if socket closed 	cliList.remove(newCli); */
 				}
-
 			}
 
+			public void writeToClient(final OutputStream os,
+					LinkedList<ByteBuffer> bufs, int inputIndex)
+					throws IOException {
+				while(inputIndex < bufs.size()) {
+					ByteBuffer b = bufs.get(inputIndex++);
+					os.write(b.array(), b.position(), b.limit());
+				}
+				os.flush();
+			}
 		};
+		clients++;
 		new Thread(sender).start();
 
 	}
+
+
+	public void dump32(LinkedList<ByteBuffer>bufs) {
+		int len =0;
+		for(ByteBuffer b: bufs) 	len += b.remaining();
+		ByteBuffer top = bufs.getFirst();
+		ByteBuffer end = bufs.getLast();
+		System.err.println("length: "+len);
+		System.err.print("head 0: ");
+		for(int i = 0; i<16 && i < top.remaining(); i++) {
+			System.err.print(" "+ top.get(i));
+		}			
+		System.err.print("tail 0: ");
+		for(int i = 0; i<16 && i < end.remaining(); i++) {
+			System.err.print(" "+end.get(i));
+		}
+		System.err.println();
+	}
+
+	@Test
+	public void test1() {
+		try {
+			LinkedList<ByteBuffer> in = new LinkedList<ByteBuffer>();
+			LinkedList<ByteBuffer> out = new LinkedList<ByteBuffer>();
+			LinkedList<ByteBuffer> out2 = new LinkedList<ByteBuffer>();
+//			if (false) {
+//			for(int i=0;i<10;i++) {
+//				in.add(ByteBuffer.wrap("test1".getBytes()));
+//				in.add(ByteBuffer.wrap("test2".getBytes()));
+//				in.add(ByteBuffer.wrap("test3".getBytes()));
+//				in.add(ByteBuffer.wrap("test44".getBytes()));
+//			}
+//			} else 
+			{
+				String t = "";
+				for(int i=0;i<10;i++) {
+					t += "test1";
+					t += "test2";
+					t += "test3";
+					t += "test44";
+				}
+				in.add(ByteBuffer.wrap(t.getBytes()));
+			}
+			
+			LinkedList<ByteBuffer> in1 = clone(in);
+
+			Deflater deflater = new Deflater();
+			zip(deflater,in,0,out);
+			// LinkedList<ByteBuffer> out3 = clone(out);   zipped result is depend on deflator's state
+			unzip(inflater, out, 0,out2, INFLATE_BUFSIZE);
+		    // inflater.reset();
+			equalByteBuffers(in1, out2);
+			LinkedList<ByteBuffer> out4 = new LinkedList<ByteBuffer>();
+			deflater = new Deflater();
+			zip(deflater,out2,0,out4);
+			LinkedList<ByteBuffer> out5 = new LinkedList<ByteBuffer>();
+			unzip(inflater,out4,0, out5, INFLATE_BUFSIZE);
+			int len = equalByteBuffers(in1,out5);
+			
+			System.out.println("Test Ok. "+len);
+		} catch (Exception e) {
+			assertEquals(0,1);
+		}
+	}
+
+	private LinkedList<ByteBuffer> clone(LinkedList<ByteBuffer> in) {
+		LinkedList<ByteBuffer> copy = new LinkedList<ByteBuffer>();
+		for(ByteBuffer b: in) {
+			ByteBuffer c = b.duplicate();
+			copy.add(c);
+		}
+		return copy;
+	}
+
+
+	
+	public int equalByteBuffers(LinkedList<ByteBuffer> in,
+			LinkedList<ByteBuffer> out2) {
+		int len = 0;
+		Iterable<Byte> i = byteBufferIterator(in);
+		Iterator<Byte> o = byteBufferIterator(out2).iterator();
+
+		for(int b: i) {
+			len ++;
+			if (o.hasNext()) {
+				int c = o.next();
+				assertEquals(b,c);
+			} else 
+				assertEquals(0,1);
+		}
+		if (o.hasNext()) 
+			assertEquals(0,1);
+		// System.out.println();
+		return len;
+	}
+
+	private Iterable<Byte> byteBufferIterator(final LinkedList<ByteBuffer> in) {
+		return new Iterable<Byte>() {
+			public Iterator<Byte> iterator() {
+				return new Iterator<Byte>() {
+					int bytes = 0;
+					int buffers = 0;
+					public boolean hasNext() {
+						for(;;) {
+							if (buffers>=in.size()) return false;
+							ByteBuffer b = in.get(buffers);
+							if (! (bytes<b.remaining())) {
+								buffers ++; bytes=0;
+							} else return true;
+						}
+					}
+					public Byte next() {
+						ByteBuffer bf =in.get(buffers); 
+						byte b = bf.get(bytes++); 
+						if (bf.remaining()<=bytes) {
+							buffers++;
+							bytes = 0;
+						}
+						// System.out.print(b);
+						return b;
+					}
+					public void remove() {
+					}
+				};
+			}
+		};
+	}
+
 }
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/myVncProxy/OptionsNoFrame.java	Sat Aug 06 23:57:18 2011 +0900
@@ -0,0 +1,330 @@
+package myVncProxy;
+
+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;
+  VncProxyService 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(VncProxyService 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/myVncProxy/ProxyVncCanvas.java	Sat Aug 06 23:56:16 2011 +0900
+++ b/src/myVncProxy/ProxyVncCanvas.java	Sat Aug 06 23:57:18 2011 +0900
@@ -79,6 +79,7 @@
 
 	// True if we process keyboard and mouse events.
 	boolean inputEnabled;
+	private int b = 0;
 	
 
 	
@@ -359,29 +360,29 @@
 		//
 		// main dispatch loop
 		//
-
-
-		
 		
 		long count = 0;
 		while (true) {
-//			System.out.println("\ncount=" + count);
-
+			// System.out.println("\ncount=" + count);
+			
 			count++;
 			
-			/*
+			/**
 			 *  read Data from parents and send Data to Client.
-			 *  
 			 */
-			rfb.sendDataToClient();		
-
-			int bufSize = (int)rfb.getNumBytesRead();
+			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
@@ -451,6 +452,7 @@
 						handleHextileRect(rx, ry, rw, rh);
 						break;
 					case RfbProto.EncodingZRLE:
+					case RfbProto.EncodingZRLEE:
 						statNumRectsZRLE++;
 						handleZRLERect(rx, ry, rw, rh);
 						break;
@@ -504,8 +506,7 @@
 					setPixelFormat();
 					fullUpdateNeeded = true;
 				}
-*/				
-				
+*/
 				// Request framebuffer update if needed.
 				int w = rfb.framebufferWidth;
 				int h = rfb.framebufferHeight;
@@ -529,7 +530,7 @@
 				throw new Exception("Unknown RFB message type " + msgType);
 			}
 
-			bufSize = (int)rfb.getNumBytesRead() - bufSize;
+			int bufSize = (int)(rfb.getNumBytesRead() - numBytesRead);
 //			System.out.println("bufSize="+bufSize);
 //			rfb.bufResetSend(bufSize);
 
--- a/src/myVncProxy/RfbProto.java	Sat Aug 06 23:56:16 2011 +0900
+++ b/src/myVncProxy/RfbProto.java	Sat Aug 06 23:57:18 2011 +0900
@@ -26,12 +26,9 @@
 //
 
 import java.io.*;
-import java.awt.*;
 import java.awt.event.*;
 import java.net.Socket;
-import java.net.ServerSocket;
 import java.util.zip.*;
-import java.nio.*;
 
 class RfbProto {
 
@@ -78,8 +75,8 @@
 
 	// Supported encodings and pseudo-encodings
 	final static int EncodingRaw = 0, EncodingCopyRect = 1, EncodingRRE = 2,
-			EncodingCoRRE = 4, EncodingHextile = 5, EncodingZlib = 6,
-			EncodingTight = 7, EncodingZRLE = 16,
+ 			EncodingCoRRE = 4, EncodingHextile = 5, EncodingZlib = 6,
+			EncodingTight = 7, EncodingZRLEE = 15, EncodingZRLE = 16,
 			EncodingCompressLevel0 = 0xFFFFFF00,
 			EncodingQualityLevel0 = 0xFFFFFFE0, EncodingXCursor = 0xFFFFFF10,
 			EncodingRichCursor = 0xFFFFFF11, EncodingPointerPos = 0xFFFFFF18,
@@ -88,6 +85,7 @@
 			SigEncodingCopyRect = "COPYRECT", SigEncodingRRE = "RRE_____",
 			SigEncodingCoRRE = "CORRE___", SigEncodingHextile = "HEXTILE_",
 			SigEncodingZlib = "ZLIB____", SigEncodingTight = "TIGHT___",
+			SigEncodingZRLEE = "ZRLEE___",
 			SigEncodingZRLE = "ZRLE____",
 			SigEncodingCompressLevel0 = "COMPRLVL",
 			SigEncodingQualityLevel0 = "JPEGQLVL",
@@ -223,6 +221,10 @@
 		timeWaitedIn100us = 5;
 		timedKbits = 0;
 	}
+	
+	public RfbProto() {
+		
+	}
 
 
 
@@ -467,13 +469,16 @@
 		encodingCaps.add(EncodingHextile, StandardVendor, SigEncodingHextile,
 				"Standard Hextile encoding");
 		encodingCaps.add(EncodingZRLE, StandardVendor, SigEncodingZRLE,
-				"Standard ZRLE encoding");
+	    		"Standard ZRLE encoding");		
+		encodingCaps.add(EncodingZRLEE, StandardVendor, SigEncodingZRLEE,
+	    		"Standard ZRLE(E) encoding");
 		encodingCaps.add(EncodingZlib, TridiaVncVendor, SigEncodingZlib,
 				"Zlib encoding");
 		encodingCaps.add(EncodingTight, TightVncVendor, SigEncodingTight,
 				"Tight encoding");
 
 		// Supported pseudo-encoding types
+
 		encodingCaps.add(EncodingCompressLevel0, TightVncVendor,
 				SigEncodingCompressLevel0, "Compression level");
 		encodingCaps.add(EncodingQualityLevel0, TightVncVendor,
@@ -488,6 +493,7 @@
 				"LastRect protocol extension");
 		encodingCaps.add(EncodingNewFBSize, TightVncVendor,
 				SigEncodingNewFBSize, "Framebuffer size change");
+
 	}
 
 	//
@@ -561,6 +567,10 @@
 /*
 		if (viewer.options.shareDesktop) {
 */
+		
+		/**
+		 * shared flag
+		 */
 			os.write(1);
 //			os.write(0);
 
@@ -716,6 +726,7 @@
 
 		if (updateRectEncoding == EncodingZlib
 				|| updateRectEncoding == EncodingZRLE
+				|| updateRectEncoding == EncodingZRLEE
 				|| updateRectEncoding == EncodingTight)
 			wereZlibUpdates = true;
 
@@ -948,6 +959,8 @@
 	final static int META_MASK = InputEvent.META_MASK;
 	final static int ALT_MASK = InputEvent.ALT_MASK;
 
+
+
 	//
 	// Write a pointer event message. We may need to send modifier key events
 	// around it to set the correct modifier state.
--- a/src/myVncProxy/VncCanvas.java	Sat Aug 06 23:56:16 2011 +0900
+++ b/src/myVncProxy/VncCanvas.java	Sat Aug 06 23:57:18 2011 +0900
@@ -26,11 +26,8 @@
 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;
 
@@ -478,6 +475,7 @@
 						handleHextileRect(rx, ry, rw, rh);
 						break;
 					case RfbProto.EncodingZRLE:
+					case RfbProto.EncodingZRLEE:
 						statNumRectsZRLE++;
 						handleZRLERect(rx, ry, rw, rh);
 						break;
@@ -740,6 +738,7 @@
 
 	// These colors should be kept between handleHextileSubrect() calls.
 	private Color hextile_bg, hextile_fg;
+	boolean noZRLEdecode = false;
 
 	void handleHextileRect(int x, int y, int w, int h) throws IOException {
 
@@ -887,8 +886,8 @@
 	//
 
 	void handleZRLERect(int x, int y, int w, int h) throws Exception {
-
-		if (zrleInStream == null)
+		if (noZRLEdecode) return;
+		if (zrleInStream == null || rfb.updateRectEncoding==RfbProto.EncodingZRLEE)
 			zrleInStream = new ZlibInStream();
 //		System.out.println("zrleInStream.end="+zrleInStream.inflater.off);			
 		
--- a/src/myVncProxy/VncProxyService.java	Sat Aug 06 23:56:16 2011 +0900
+++ b/src/myVncProxy/VncProxyService.java	Sat Aug 06 23:57:18 2011 +0900
@@ -15,7 +15,6 @@
 
 		v.init();
 		v.start_threads();
-		
 	}
 	
 	String[] mainArgs;
@@ -38,7 +37,8 @@
 	ButtonPanel buttonPanel;
 	Label connStatusLabel;
 	ProxyVncCanvas vc;
-	OptionsFrame options;
+//	OptionsFrame options;
+	OptionsNoFrame options;
 	ClipboardFrame clipboard;
 	RecordingFrame rec;
 
@@ -85,6 +85,7 @@
 
 		readParameters();
 
+		options = new OptionsNoFrame(this);
 		recordingSync = new Object();
 
 		sessionFileName = null;
@@ -105,7 +106,7 @@
 		}catch(Exception e){}
 
 		rfbThread = new Thread(this);
-		accThread = new Thread(new AcceptThread(rfb)); 
+		accThread = new Thread(new AcceptThread(rfb, 5999)); 
 
 	}
 	
@@ -352,11 +353,11 @@
 		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;
 		}
@@ -369,6 +370,7 @@
 		if (preferredEncoding != RfbProto.EncodingZlib) {
 			encodings[nEncodings++] = RfbProto.EncodingZlib;
 		}
+		/*
 		if (preferredEncoding != RfbProto.EncodingCoRRE) {
 			encodings[nEncodings++] = RfbProto.EncodingCoRRE;
 		}
@@ -384,14 +386,14 @@
 			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;
 
@@ -676,7 +678,7 @@
 
 		if (rfb != null && !rfb.closed())
 			rfb.close();
-		options.dispose();
+//		options.dispose();
 		clipboard.dispose();
 		if (rec != null)
 			rec.dispose();
@@ -762,7 +764,7 @@
 		System.out.println("Destroying applet");
 
 		vncContainer.removeAll();
-		options.dispose();
+//		options.dispose();
 		clipboard.dispose();
 		if (rec != null)
 			rec.dispose();