changeset 15:89e1c5f84407

faile ZRLE encode. success raw encode.
author e085711
date Sun, 17 Apr 2011 19:33:01 +0900
parents 57f227139599
children 549229fd7288
files src/MyRfbProto.java src/ProxyVncCanvas.java src/VncCanvas.java src/VncProxyService.java
diffstat 4 files changed, 1663 insertions(+), 1687 deletions(-) [+]
line wrap: on
line diff
--- a/src/MyRfbProto.java	Sun Apr 17 02:16:52 2011 +0900
+++ b/src/MyRfbProto.java	Sun Apr 17 19:33:01 2011 +0900
@@ -1,9 +1,8 @@
-import java.io.BufferedInputStream;
-import java.io.DataInputStream;
+import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.net.ServerSocket;
 import java.net.Socket;
-import java.nio.ByteBuffer;
 import java.util.LinkedList;
 
 
@@ -12,8 +11,6 @@
 	private ServerSocket servSock; 	
 	private byte initData[];
 	private LinkedList <Socket> cliList;
-	boolean MYVNC = true;
-	
 	
 	MyRfbProto(String h, int p, VncViewer v ) throws IOException {
 		super(h, p, v);
@@ -44,10 +41,12 @@
 	
 	void mark(int len) throws IOException {
 		is.mark(len);
+		System.out.println("is.mark");
 	}
 
 	void reset() throws IOException {
 		is.reset();
+		System.out.println("is.reset");
 	}
 
 	boolean markSupported() {
@@ -108,9 +107,31 @@
 		for(Socket cli : cliList)
 			cli.getOutputStream().write(b, 0, b.length);		
 	}	
+	boolean ready() throws IOException {
+		BufferedReader br = new BufferedReader(new InputStreamReader(is));
+		return br.ready();
+	}	
 
 	int cliSize(){
 		return cliList.size();
 	}	
+	void printNumBytesRead(){
+		System.out.println("numBytesRead="+numBytesRead);
+	}	
+	void bufResetSend(int size) throws IOException {
+		reset();
+		int len = size;
+		if(available() < size )
+			len = available();
+		System.out.println("len="+len);
+		byte buffer[] = new byte[len];
+		readFully(buffer);
+		sendData(buffer);
+	}
+	void resetNumBytesRead(){
+		numBytesRead = 0;
+	}
+
+	
 	
 }
--- a/src/ProxyVncCanvas.java	Sun Apr 17 02:16:52 2011 +0900
+++ b/src/ProxyVncCanvas.java	Sun Apr 17 19:33:01 2011 +0900
@@ -1,5 +1,3 @@
-
-
 import java.awt.*;
 import java.awt.event.*;
 import java.awt.image.*;
@@ -15,1048 +13,995 @@
 //
 
 class ProxyVncCanvas extends Canvas implements KeyListener, MouseListener,
-	MouseMotionListener {
+		MouseMotionListener {
 
-VncProxyService viewer;
-MyRfbProto rfb;
-ColorModel cm8, cm24;
-Color[] colors;
-int bytesPixel;
+	VncProxyService viewer;
+	MyRfbProto rfb;
+	ColorModel cm8, cm24;
+	Color[] colors;
+	int bytesPixel;
 
-int maxWidth = 0, maxHeight = 0;
-int scalingFactor;
-int scaledWidth, scaledHeight;
+	int maxWidth = 0, maxHeight = 0;
+	int scalingFactor;
+	int scaledWidth, scaledHeight;
 
-Image memImage;
-Graphics memGraphics;
+	Image memImage;
+	Graphics memGraphics;
 
-Image rawPixelsImage;
-MemoryImageSource pixelsSource;
-byte[] pixels8;
-int[] pixels24;
+	Image rawPixelsImage;
+	MemoryImageSource pixelsSource;
+	byte[] pixels8;
+	int[] pixels24;
 
-// Update statistics.
-long statStartTime; // time on first framebufferUpdateRequest
-int statNumUpdates; // counter for FramebufferUpdate messages
-int statNumTotalRects; // rectangles in FramebufferUpdate messages
-int statNumPixelRects; // the same, but excluding pseudo-rectangles
-int statNumRectsTight; // Tight-encoded rectangles (including JPEG)
-int statNumRectsTightJPEG; // JPEG-compressed Tight-encoded rectangles
-int statNumRectsZRLE; // ZRLE-encoded rectangles
-int statNumRectsHextile; // Hextile-encoded rectangles
-int statNumRectsRaw; // Raw-encoded rectangles
-int statNumRectsCopy; // CopyRect rectangles
-int statNumBytesEncoded; // number of bytes in updates, as received
-int statNumBytesDecoded; // number of bytes, as if Raw encoding was used
+	// 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;
 
-// 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;
 
-// Zlib encoder's data.
-byte[] zlibBuf;
-int zlibBufLen = 0;
-Inflater zlibInflater;
+	// Tight encoder's data.
+	final static int tightZlibBufferSize = 512;
+	Inflater[] tightInflaters;
 
-// 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;
 
-// 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;
 
-// True if we process keyboard and mouse events.
-boolean inputEnabled;
+	
+	//
+	// The constructors.
+	//
 
-//
-// The constructors.
-//
+	public ProxyVncCanvas(VncProxyService v, int maxWidth_, int maxHeight_)
+			throws IOException {
 
-public ProxyVncCanvas(VncProxyService v, int maxWidth_, int maxHeight_)
-		throws IOException {
+		viewer = v;
+		maxWidth = maxWidth_;
+		maxHeight = maxHeight_;
 
-	viewer = v;
-	maxWidth = maxWidth_;
-	maxHeight = maxHeight_;
+		rfb = viewer.rfb;
 
-	rfb = viewer.rfb;
+		tightInflaters = new Inflater[4];
 
-	tightInflaters = new Inflater[4];
+		cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6));
+		cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
 
-	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));
 
-	colors = new Color[256];
-	for (int i = 0; i < 256; i++)
-		colors[i] = new Color(cm8.getRGB(i));
+		// setPixelFormat();
 
-//	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);
+	}
 
-	inputEnabled = false;
-	// Keyboard listener is enabled even in view-only mode, to catch
-	// 'r' or 'R' key presses used to request screen update.
-	addKeyListener(this);
-}
+	public ProxyVncCanvas(VncProxyService v) throws IOException {
+		this(v, 0, 0);
+	}
+
+	//
+	// Callback methods to determine geometry of our Component.
+	//
 
-public ProxyVncCanvas(VncProxyService v) throws IOException {
-	this(v, 0, 0);
-}
+	public Dimension getPreferredSize() {
+		return new Dimension(scaledWidth, scaledHeight);
+	}
 
-//
-// Callback methods to determine geometry of our Component.
-//
+	public Dimension getMinimumSize() {
+		return new Dimension(scaledWidth, scaledHeight);
+	}
 
-public Dimension getPreferredSize() {
-	return new Dimension(scaledWidth, scaledHeight);
-}
+	public Dimension getMaximumSize() {
+		return new Dimension(scaledWidth, scaledHeight);
+	}
 
-public Dimension getMinimumSize() {
-	return new Dimension(scaledWidth, scaledHeight);
-}
+	//
+	// All painting is performed here.
+	//
 
-public Dimension getMaximumSize() {
-	return new Dimension(scaledWidth, scaledHeight);
-}
-
-//
-// All painting is performed here.
-//
+	public void update(Graphics g) {
+		paint(g);
+	}
 
-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);
+	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);
+			}
 		}
 	}
-	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);
 	}
-}
 
-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.
+	//
 
-//
-// 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();
+	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();
+			return false; // All image data was processed.
 		}
 	}
 
-	// Images with raw pixels should be re-allocated on every change
-	// of geometry or pixel format.
-	if (bytesPixel == 1) {
+	//
+	// 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.
+	//
 
-		pixels24 = null;
-		pixels8 = new byte[fbWidth * fbHeight];
+	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
+		}
+	}
 
-		pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm8,
-				pixels8, 0, fbWidth);
+	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;
 
-		zrleTilePixels24 = null;
-		zrleTilePixels8 = new byte[64 * 64];
+		// 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;
 
-	} else {
+		// 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();
+			}
+		}
 
-		pixels8 = null;
-		pixels24 = new int[fbWidth * fbHeight];
+		// 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];
 
-		pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm24,
-				pixels24, 0, fbWidth);
+		} else {
+
+			pixels8 = null;
+			pixels24 = new int[fbWidth * fbHeight];
 
-		zrleTilePixels8 = null;
-		zrleTilePixels24 = new int[64 * 64];
+			pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm24,
+					pixels24, 0, fbWidth);
+
+			zrleTilePixels8 = null;
+			zrleTilePixels24 = new int[64 * 64];
+
+		}
+		pixelsSource.setAnimated(true);
+		rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource);
 
 	}
-	pixelsSource.setAnimated(true);
-	rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource);
 
-}
-
-void resizeDesktopFrame() {
-	setSize(scaledWidth, scaledHeight);
+	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));
+		// 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();
+		viewer.vncFrame.pack();
+
+		// Try to limit the frame size to the screen size.
 
-	// Try to limit the frame size to the screen size.
+		Dimension screenSize = viewer.vncFrame.getToolkit().getScreenSize();
+		Dimension frameSize = viewer.vncFrame.getSize();
+		Dimension newSize = frameSize;
 
-	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.
 
-	// 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;
 
-	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);
+		}
 
-	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();
 	}
 
-	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
+	// processNormalProtocol() - executed by the rfbThread to deal with the
+	// RFB socket.
 	//
 
-	long count = 0;
-	rfb.initServSock(5550);
-	try {
+	public void processNormalProtocol() throws Exception {
+
+		// Start/stop session recording if necessary.
+		viewer.checkRecordingStatus();
+
+		rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth,
+				rfb.framebufferHeight, false);
+
+		resetStats();
+		boolean statsRestarted = false;
+
+		//
+		// main dispatch loop
+		//
+
+		long count = 0;
+		rfb.initServSock(5550);
+
+		// single thread
+		/*
+		try {
 			// rfb.setSoTimeout(1000);
 			Socket newCli = rfb.accept();
 			rfb.sendInitData(newCli);
 			rfb.addSock(newCli);
 		} catch (IOException e) {
-	}
-	/*
-	 * Thread accept = new Thread(new acceptThread(rfb)); accept.start();
-	 */
-	while (true) {
+		}
+*/
+		// multi thread
+		Thread accept = new Thread(new acceptThread(rfb));
+		 accept.start();
+		 
+
+		while (true) {
+
+			System.out.println("\ncount=" + count);
+			count++;
+			
+			rfb.mark(16);
+			int mesageType = rfb.readU8();
+			System.out.println("mesageType=" + mesageType);
+			rfb.skipBytes(11);
+			int encoding = rfb.readU32();
+			System.out.println("encoding=" + encoding);
+			rfb.reset();
+
+			rfb.mark(rfb.available());
+
+			rfb.printNumBytesRead();
+			int bufSize = (int)rfb.getNumBytesRead();
+			
+			// Read message type from the server. 
+			int msgType = rfb.readServerMessageType();
+
+			// Process the message depending on its type.
+			switch (msgType) {
+			case RfbProto.FramebufferUpdate:
 
-		if (rfb.MYVNC) {
-			if (rfb.cliSize() > 0) {
-				System.out.println("\ncount=" + count);
+				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++) {
 
-				int nBytes = 0;
-				rfb.mark(20);
-				int msgType = rfb.readU8();
-				System.out.println("msgType=" + msgType);
+					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;
+					}
 
-				rfb.skipBytes(11);
-				int encoding = rfb.readU32();
-				System.out.println("encoding=" + encoding);
-				nBytes = rfb.readU32();
-				System.out.println("nBytes=" + nBytes);
-				rfb.reset();
+					if (rfb.updateRectEncoding == rfb.EncodingPointerPos) {
+						softCursorMove(rx, ry);
+						cursorPosReceived = true;
+						continue;
+					}
 
-				int len = rfb.available();
-				System.out.println("rfb.available()=" + len);
-				if (len > 0) {
+					long numBytesReadBefore = rfb.getNumBytesRead();
+
+					rfb.startTiming();
 
-					if (nBytes > 0 & encoding == 16) {// 0より大きい(データがある)ときデータを転送
-
-						rfb.mark(nBytes + 20);
+					switch (rfb.updateRectEncoding) {
+					case RfbProto.EncodingRaw:
+						statNumRectsRaw++;
+						handleRawRect(rx, ry, rw, rh);
+						rfb.printNumBytesRead();
+						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);
+					}
 
-						byte b[] = new byte[nBytes + 20];
-						// byte b[] = new byte[18+rfb.rnBytes];
-						System.out.println("b.length=" + b.length);
+					rfb.stopTiming();
+
+					statNumPixelRects++;
+					statNumBytesDecoded += rw * rh * bytesPixel;
+					statNumBytesEncoded += (int) (rfb.getNumBytesRead() - numBytesReadBefore);
+				}
+
+				boolean fullUpdateNeeded = false;
 
-						rfb.readFully(b);
+				// Start/stop session recording if necessary. Request full
+				// update if a new session file was opened.
+				if (viewer.checkRecordingStatus())
+					fullUpdateNeeded = true;
 
-						// rfb.cliSock.getOutputStream().write(b, 0,
-						// b.length);
-						rfb.sendData(b);
+				// Defer framebuffer update request if necessary. But wake up
+				// immediately on keyboard or mouse event. Also, don't sleep
+				// if there is some data to receive, or if the last update
+				// included a PointerPos message.
+				if (viewer.deferUpdateRequests > 0 && rfb.available() == 0
+						&& !cursorPosReceived) {
+					synchronized (rfb) {
+						try {
+							rfb.wait(viewer.deferUpdateRequests);
+						} catch (InterruptedException e) {
+						}
 					}
 				}
+
+				viewer.autoSelectEncodings();
+
+				// Before requesting framebuffer update, check if the pixel
+				// format should be changed.
+/*
+				if (viewer.options.eightBitColors != (bytesPixel == 1)) {
+					// Pixel format should be changed.
+					setPixelFormat();
+					fullUpdateNeeded = true;
+				}
+*/
+				// Request framebuffer update if needed.
+				int w = rfb.framebufferWidth;
+				int h = rfb.framebufferHeight;
+				rfb.writeFramebufferUpdateRequest(0, 0, w, h, !fullUpdateNeeded);
+
+				break;
+
+			case RfbProto.SetColourMapEntries:
+				throw new Exception("Can't handle SetColourMapEntries message");
+
+			case RfbProto.Bell:
+				Toolkit.getDefaultToolkit().beep();
+				break;
+
+			case RfbProto.ServerCutText:
+				String s = rfb.readServerCutText();
+				viewer.clipboard.setCutText(s);
+				break;
+
+			default:
+				throw new Exception("Unknown RFB message type " + msgType);
+			}
+			bufSize = (int)rfb.getNumBytesRead() - bufSize;
+			rfb.bufResetSend(bufSize);
+		}
+	}
+
+	//
+	// Handle a raw rectangle. The second form with paint==false is used
+	// by the Hextile decoder for raw-encoded tiles.
+	//
+
+	void handleRawRect(int x, int y, int w, int h) throws IOException {
+		handleRawRect(x, y, w, h, true);
+	}
+
+	void handleRawRect(int x, int y, int w, int h, boolean paint)
+			throws IOException {
+
+		if (bytesPixel == 1) {
+			for (int dy = y; dy < y + h; dy++) {
+				rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w);
+				if (rfb.rec != null) {
+					rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w);
+				}
+			}
+		} else {
+			byte[] buf = new byte[w * 4];
+			int i, offset;
+			for (int dy = y; dy < y + h; dy++) {
+				rfb.readFully(buf);
+				if (rfb.rec != null) {
+					rfb.rec.write(buf);
+				}
+				/*
+				 * offset = dy * rfb.framebufferWidth + x; for (i = 0; i < w;
+				 * i++) { pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16 |
+				 * (buf[i * 4 + 1] & 0xFF) << 8 | (buf[i * 4] & 0xFF); }
+				 */
 			}
 		}
-		byte buffer[] = new byte[rfb.available()];
-		rfb.readFully(buffer);
-		rfb.sendData(buffer);
-		
-		count++;
+		/*
+		 * 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();
 
-/*
-		// Read message type from the server.
-		int msgType = rfb.readServerMessageType();
+		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);
+	}
 
-		// Process the message depending on its type.
-		switch (msgType) {
-		case RfbProto.FramebufferUpdate:
+	//
+	// 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;
 
-			if (statNumUpdates == viewer.debugStatsExcludeUpdates
-					&& !statsRestarted) {
-				resetStats();
-				statsRestarted = true;
-			} else if (statNumUpdates == viewer.debugStatsMeasureUpdates
-					&& statsRestarted) {
-				viewer.disconnect();
+		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);
 			}
 
-			rfb.readFramebufferUpdate();
-			statNumUpdates++;
+			// 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);
+		}
 
-			boolean cursorPosReceived = false;
+		// Is it a raw-encoded sub-rectangle?
+		if ((subencoding & rfb.HextileRaw) != 0) {
+			handleRawRect(tx, ty, tw, th, false);
+			return;
+		}
 
-			for (int i = 0; i < rfb.updateNRects; i++) {
+		// 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);
 
-				rfb.readFramebufferUpdateRectHdr();
-				statNumTotalRects++;
-				int rx = rfb.updateRectX, ry = rfb.updateRectY;
-				int rw = rfb.updateRectW, rh = rfb.updateRectH;
+		// 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;
 
-				if (rfb.updateRectEncoding == rfb.EncodingLastRect)
-					break;
+		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) {
 
-				if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) {
-					rfb.setFramebufferSize(rw, rh);
-					updateFramebufferSize();
-					break;
-				}
+			// 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();
 
-				if (rfb.updateRectEncoding == rfb.EncodingXCursor
-						|| rfb.updateRectEncoding == rfb.EncodingRichCursor) {
-					handleCursorShapeUpdate(rfb.updateRectEncoding, rx, ry,
-							rw, rh);
-					continue;
-				}
+		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;
+			}
 
-				if (rfb.updateRectEncoding == rfb.EncodingPointerPos) {
-					softCursorMove(rx, ry);
-					cursorPosReceived = 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;
 				}
 
-				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) {
+				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);
 					}
 				}
-			}
-
-			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);
-		}
-*/
-	}
-}
-
-//
-// 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);
+				handleUpdatedZrleTile(tx, ty, tw, th);
 			}
 		}
-	} 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));
+		zrleInStream.reset();
 
-	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);
 	}
 
-	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();
+	int readPixel(InStream is) throws Exception {
+		int pix;
 
-	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 (bytesPixel == 1) {
 
-	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];
+			pix = is.readU8();
 		} else {
-			pixel = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF,
-					buf[i] & 0xFF);
-			i += 4;
+			int p1 = is.readU8();
+			int p2 = is.readU8();
+			int p3 = is.readU8();
+			pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF);
 		}
-		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);
+		return pix;
 	}
 
-	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);
+	void readPixels(InStream is, int[] dst, int count) throws Exception {
+		int pix;
 		if (bytesPixel == 1) {
-			hextile_bg = colors[cbuf[0] & 0xFF];
+			byte[] buf = new byte[count];
+			is.readBytes(buf, 0, count);
+			for (int i = 0; i < count; i++) {
+				dst[i] = (int) buf[i] & 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);
+			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));
+			}
 		}
 	}
 
-	// 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);
+	void readZrlePalette(int[] palette, int palSize) throws Exception {
+		readPixels(zrleInStream, palette, palSize);
 	}
 
-	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);
+	void readZrleRawPixels(int tw, int th) throws Exception {
+		if (bytesPixel == 1) {
+			zrleInStream.readBytes(zrleTilePixels8, 0, tw * th);
+		} else {
+			readPixels(zrleInStream, zrleTilePixels24, tw * th); // /
 		}
 	}
 
-	zrleInStream.reset();
-
-	scheduleRepaint(x, y, w, h);
-}
-
-int readPixel(InStream is) throws Exception {
-	int pix;
-
-	if (bytesPixel == 1) {
+	void readZrlePackedPixels(int tw, int th, int[] palette, int palSize)
+			throws Exception {
 
-		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;
-}
+		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;
 
-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];
+			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) {
+	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();
@@ -1065,541 +1010,595 @@
 
 			if (!(len <= end - ptr))
 				throw new Exception("ZRLE decoder: assertion failed"
-						+ " (len <= end - ptr)");
-		}
-
-		index &= 127;
-		int pix = palette[index];
+						+ " (len <= end-ptr)");
 
-		if (bytesPixel == 1) {
-			while (len-- > 0)
-				zrleTilePixels8[ptr++] = (byte) pix;
-		} else {
-			while (len-- > 0)
-				zrleTilePixels24[ptr++] = pix;
+			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 readZrlePackedRLEPixels(int tw, int th, int[] palette)
+			throws Exception {
 
-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();
+		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 (zlibBuf == null || zlibBufLen < nBytes) {
-		zlibBufLen = nBytes * 2;
-		zlibBuf = new byte[zlibBufLen];
-	}
-
-	rfb.readFully(zlibBuf, 0, nBytes);
+				if (!(len <= end - ptr))
+					throw new Exception("ZRLE decoder: assertion failed"
+							+ " (len <= end - ptr)");
+			}
 
-	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);
+			index &= 127;
+			int pix = palette[index];
 
-	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 (bytesPixel == 1) {
+				while (len-- > 0)
+					zrleTilePixels8[ptr++] = (byte) pix;
+			} else {
+				while (len-- > 0)
+					zrleTilePixels24[ptr++] = pix;
 			}
-			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 {
+	//
+	// Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update.
+	//
 
-	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);
+	void handleUpdatedZrleTile(int x, int y, int w, int h) {
+		Object src, dst;
+		if (bytesPixel == 1) {
+			src = zrleTilePixels8;
+			dst = pixels8;
 		} else {
-			// Tell the decoder to flush each of the four zlib streams.
-			rfb.rec.writeByte(comp_ctl | 0x0F);
+			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);
 	}
 
-	// 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;
-	}
+	//
+	// Handle a Zlib-encoded rectangle.
+	//
+
+	void handleZlibRect(int x, int y, int w, int h) throws Exception {
+
+		int nBytes = rfb.readU32();
 
-	// Check correctness of subencoding value.
-	if (comp_ctl > rfb.TightMaxSubencoding) {
-		throw new Exception("Incorrect tight subencoding: " + comp_ctl);
-	}
+		if (zlibBuf == null || zlibBufLen < nBytes) {
+			zlibBufLen = nBytes * 2;
+			zlibBuf = new byte[zlibBufLen];
+		}
+
+		rfb.readFully(zlibBuf, 0, nBytes);
 
-	// Handle solid-color rectangles.
-	if (comp_ctl == rfb.TightFill) {
+		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) {
-			int idx = rfb.readU8();
-			memGraphics.setColor(colors[idx]);
-			if (rfb.rec != null) {
-				rfb.rec.writeByte(idx);
+			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[3];
-			rfb.readFully(buf);
-			if (rfb.rec != null) {
-				rfb.rec.write(buf);
+			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);
 			}
-			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);
+
+		handleUpdatedPixels(x, y, w, h);
 		scheduleRepaint(x, y, w, h);
-		return;
-
 	}
 
-	if (comp_ctl == rfb.TightJpeg) {
-
-		statNumRectsTightJPEG++;
+	//
+	// Handle a Tight-encoded rectangle.
+	//
 
-		// 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);
-		}
+	void handleTightRect(int x, int y, int w, int h) throws Exception {
 
-		// 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");
+		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);
 			}
 		}
 
-		// Done, jpegRect is not needed any more.
-		jpegRect = null;
-		return;
-
-	}
+		// 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;
+		}
 
-	// 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);
+		// Check correctness of subencoding value.
+		if (comp_ctl > rfb.TightMaxSubencoding) {
+			throw new Exception("Incorrect tight subencoding: " + comp_ctl);
 		}
-		if (filter_id == rfb.TightFilterPalette) {
-			numColors = rfb.readU8() + 1;
-			if (rfb.rec != null) {
-				rfb.rec.writeByte(numColors - 1);
-			}
+
+		// Handle solid-color rectangles.
+		if (comp_ctl == rfb.TightFill) {
+
 			if (bytesPixel == 1) {
-				if (numColors != 2) {
-					throw new Exception("Incorrect tight palette size: "
-							+ numColors);
-				}
-				rfb.readFully(palette8);
+				int idx = rfb.readU8();
+				memGraphics.setColor(colors[idx]);
 				if (rfb.rec != null) {
-					rfb.rec.write(palette8);
+					rfb.rec.writeByte(idx);
 				}
 			} else {
-				byte[] buf = new byte[numColors * 3];
+				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);
 				}
-				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.
+				decodeGradientData(x, y, w, h, buf);
+			} else {
+				// Raw truecolor data.
 				if (bytesPixel == 1) {
-					decodeMonoData(x, y, w, h, indexedData, palette8);
+					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 {
-					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];
+					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 if (useGradient) {
-			// "Gradient"-processed data
-			byte[] buf = new byte[w * h * 3];
-			rfb.readFully(buf);
-			if (rfb.rec != null) {
-				rfb.rec.write(buf);
+		} 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();
 			}
-			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);
+			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 {
-				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 {
+					// 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 {
-		// 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 if (useGradient) {
+				// Compressed "Gradient"-filtered data (assuming bytesPixel ==
+				// 4).
+				decodeGradientData(x, y, w, h, buf);
 			} 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];
+				// 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 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;
+				} 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);
+		}
 	}
 
-	handleUpdatedPixels(x, y, w, h);
-	scheduleRepaint(x, y, w, h);
-}
+	//
+	// Display newly updated area of pixels.
+	//
+
+	void handleUpdatedPixels(int x, int y, int w, int 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;
+		// 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);
+	}
 
-	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);
-	}
-}
+	//
+	// Tell JVM to repaint specified desktop area.
+	//
 
-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];
+	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);
 		}
-		for (n = 7; n >= 8 - w % 8; n--) {
-			pixels24[i++] = palette[src[dy * rowBytes + dx] >> n & 1];
-		}
-		i += (rfb.framebufferWidth - w);
 	}
-}
+
+	//
+	// Handle events.
+	//
+
+	public void keyPressed(KeyEvent evt) {
+		processLocalKeyEvent(evt);
+	}
 
-//
-// Decode data processed with the "Gradient" filter.
-//
-
-void decodeGradientData(int x, int y, int w, int h, byte[] buf) {
+	public void keyReleased(KeyEvent evt) {
+		processLocalKeyEvent(evt);
+	}
 
-	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];
+	public void keyTyped(KeyEvent evt) {
+		evt.consume();
+	}
+
+	public void mousePressed(MouseEvent evt) {
+		processLocalMouseEvent(evt, false);
+	}
 
-	int offset = y * rfb.framebufferWidth + x;
+	public void mouseReleased(MouseEvent evt) {
+		processLocalMouseEvent(evt, false);
+	}
 
-	for (dy = 0; dy < h; dy++) {
+	public void mouseMoved(MouseEvent evt) {
+		processLocalMouseEvent(evt, true);
+	}
+
+	public void mouseDragged(MouseEvent evt) {
+		processLocalMouseEvent(evt, true);
+	}
 
-		/* 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;
+	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();
+					}
 				}
-				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();
 				}
 			}
-		} else {
-			// Input enabled.
+		}
+		// 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.writeKeyEvent(evt);
+					rfb.writePointerEvent(evt);
 				} catch (Exception e) {
 					e.printStackTrace();
 				}
@@ -1607,197 +1606,188 @@
 			}
 		}
 	}
-	// Don't ever pass keyboard events to AWT for default processing.
-	// Otherwise, pressing Tab would switch focus to ButtonPanel etc.
-	evt.consume();
-}
+
+	//
+	// Ignored events.
+	//
+
+	public void mouseClicked(MouseEvent evt) {
+	}
+
+	public void mouseEntered(MouseEvent evt) {
+	}
+
+	public void mouseExited(MouseEvent evt) {
+	}
+
+	//
+	// Reset update statistics.
+	//
 
-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();
-		}
+	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;
 	}
-}
 
-//
-// Ignored events.
-//
+	// ////////////////////////////////////////////////////////////////
+	//
+	// Handle cursor shape updates (XCursor and RichCursor encodings).
+	//
 
-public void mouseClicked(MouseEvent evt) {
-}
+	boolean showSoftCursor = false;
 
-public void mouseEntered(MouseEvent evt) {
-}
+	MemoryImageSource softCursorSource;
+	Image softCursor;
 
-public void mouseExited(MouseEvent evt) {
-}
-
-//
-// Reset update statistics.
-//
+	int cursorX = 0, cursorY = 0;
+	int cursorWidth, cursorHeight;
+	int origCursorWidth, origCursorHeight;
+	int hotX, hotY;
+	int origHotX, origHotY;
 
-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 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;
 
-// ////////////////////////////////////////////////////////////////
-//
-// Handle cursor shape updates (XCursor and RichCursor encodings).
-//
+		// Ignore cursor shape data if requested by user.
+		if (viewer.options.ignoreCursorUpdates) {
+			int bytesPerRow = (width + 7) / 8;
+			int bytesMaskData = bytesPerRow * height;
 
-boolean showSoftCursor = false;
+			if (encodingType == rfb.EncodingXCursor) {
+				rfb.skipBytes(6 + bytesMaskData * 2);
+			} else {
+				// rfb.EncodingRichCursor
+				rfb.skipBytes(width * height * bytesPixel + bytesMaskData);
+			}
+			return;
+		}
 
-MemoryImageSource softCursorSource;
-Image softCursor;
+		// Decode cursor pixel data.
+		softCursorSource = decodeCursorShape(encodingType, width, height);
 
-int cursorX = 0, cursorY = 0;
-int cursorWidth, cursorHeight;
-int origCursorWidth, origCursorHeight;
-int hotX, hotY;
-int origHotX, origHotY;
+		// Set original (non-scaled) cursor dimensions.
+		origCursorWidth = width;
+		origCursorHeight = height;
+		origHotX = xhot;
+		origHotY = yhot;
+
+		// Create off-screen cursor image.
+		createSoftCursor();
 
-//
-// Handle cursor shape update (XCursor and RichCursor encodings).
-//
-
-synchronized void handleCursorShapeUpdate(int encodingType, int xhot,
-		int yhot, int width, int height) throws IOException {
+		// Show the cursor.
+		showSoftCursor = true;
+		repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY,
+				cursorWidth, cursorHeight);
+	}
 
-	softCursorFree();
+	//
+	// decodeCursorShape(). Decode cursor pixel data and return
+	// corresponding MemoryImageSource instance.
+	//
 
-	if (width * height == 0)
-		return;
+	synchronized MemoryImageSource decodeCursorShape(int encodingType,
+			int width, int height) throws IOException {
 
-	// Ignore cursor shape data if requested by user.
-	if (viewer.options.ignoreCursorUpdates) {
 		int bytesPerRow = (width + 7) / 8;
 		int bytesMaskData = bytesPerRow * height;
 
+		int[] softCursorPixels = new int[width * 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();
+			// 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)) };
 
-	// Show the cursor.
-	showSoftCursor = true;
-	repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY,
-			cursorWidth, cursorHeight);
-}
-
-//
-// decodeCursorShape(). Decode cursor pixel data and return
-// corresponding MemoryImageSource instance.
-//
+			// Read pixel and mask data.
+			byte[] pixBuf = new byte[bytesMaskData];
+			rfb.readFully(pixBuf);
+			byte[] maskBuf = new byte[bytesMaskData];
+			rfb.readFully(maskBuf);
 
-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];
+			// 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;
 				}
 			}
-			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
 
-	} 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);
 
-		// 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) {
+			// 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 {
@@ -1812,99 +1802,83 @@
 					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);
 	}
 
-	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;
 
-//
-// createSoftCursor(). Assign softCursor new Image (scaled if necessary).
-// Uses softCursorSource as a source for new cursor image.
-//
+		int scaleCursor = viewer.options.scaleCursor;
+		if (scaleCursor == 0 || !inputEnabled)
+			scaleCursor = 100;
 
-synchronized void createSoftCursor() {
-
-	if (softCursorSource == null)
-		return;
+		// Save original cursor coordinates.
+		int x = cursorX - hotX;
+		int y = cursorY - hotY;
+		int w = cursorWidth;
+		int h = cursorHeight;
 
-	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);
 
-	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 (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);
+		}
 	}
 
-	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);
+	//
+	// softCursorMove(). Moves soft cursor into a particular location.
+	//
 
-		repaint(viewer.deferCursorUpdates, x, y, w, h);
+	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);
+		}
 	}
 }
-
-//
-// softCursorMove(). Moves soft cursor into a particular location.
-//
-
-synchronized void softCursorMove(int x, int y) {
-	int oldX = cursorX;
-	int oldY = cursorY;
-	cursorX = x;
-	cursorY = y;
-	if (showSoftCursor) {
-		repaint(viewer.deferCursorUpdates, oldX - hotX, oldY - hotY,
-				cursorWidth, cursorHeight);
-		repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY,
-				cursorWidth, cursorHeight);
-	}
-}
-
-//
-// softCursorFree(). Remove soft cursor, dispose resources.
-//
-
-synchronized void softCursorFree() {
-	if (showSoftCursor) {
-		showSoftCursor = false;
-		softCursor = null;
-		softCursorSource = null;
-
-		repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY,
-				cursorWidth, cursorHeight);
-	}
-}
-}
--- a/src/VncCanvas.java	Sun Apr 17 02:16:52 2011 +0900
+++ b/src/VncCanvas.java	Sun Apr 17 19:33:01 2011 +0900
@@ -381,63 +381,36 @@
 
 		long count = 0;
 		rfb.initServSock(5550);
+/*
 		try {
-				// rfb.setSoTimeout(1000);
-				Socket newCli = rfb.accept();
-				rfb.sendInitData(newCli);
-				rfb.addSock(newCli);
-			} catch (IOException e) {
+			// rfb.setSoTimeout(1000);
+			Socket newCli = rfb.accept();
+			rfb.sendInitData(newCli);
+			rfb.addSock(newCli);
+		} catch (IOException e) {
 		}
-		/*
-		 * Thread accept = new Thread(new acceptThread(rfb)); accept.start();
-		 */
+*/
+		 Thread accept = new Thread(new acceptThread(rfb)); 
+		 accept.start();
+
 		while (true) {
 
-			if (rfb.MYVNC) {
-				if (rfb.cliSize() > 0) {
-					System.out.println("\ncount=" + count);
-
-					int nBytes = 0;
-					rfb.mark(20);
-					int msgType = rfb.readU8();
-					System.out.println("msgType=" + msgType);
-
-					rfb.skipBytes(11);
-					int encoding = rfb.readU32();
-					System.out.println("encoding=" + encoding);
-					nBytes = rfb.readU32();
-					System.out.println("nBytes=" + nBytes);
-					rfb.reset();
-
-					int len = rfb.available();
-					System.out.println("rfb.available()=" + len);
-					if (len > 0) {
-
-						if (nBytes > 0 & encoding == 16) {// 0より大きい(データがある)ときデータを転送
+			System.out.println("\ncount=" + count);
+			count++;
+			
+			rfb.mark(16);
+			int mesageType = rfb.readU8();
+			System.out.println("mesageType=" + mesageType);
+			rfb.skipBytes(11);
+			int encoding = rfb.readU32();
+			System.out.println("encoding=" + encoding);
+			rfb.reset();
 
-							rfb.mark(nBytes + 20);
-
-							byte b[] = new byte[nBytes + 20];
-							// byte b[] = new byte[18+rfb.rnBytes];
-							System.out.println("b.length=" + b.length);
-
-							rfb.readFully(b);
-
-							// rfb.cliSock.getOutputStream().write(b, 0,
-							// b.length);
-							rfb.sendData(b);
+//			rfb.mark(rfb.available());
+			System.out.println("rfb.available()="+rfb.available());
 
-							try {
-								rfb.reset();
-							} catch (IOException e) {
-								System.out.println(e);
-							}
-						}
-					}
-				}
-			}
-			count++;
-
+			rfb.printNumBytesRead();
+			long bufSize = rfb.getNumBytesRead();			
 			// Read message type from the server.
 			int msgType = rfb.readServerMessageType();
 
@@ -587,7 +560,16 @@
 			default:
 				throw new Exception("Unknown RFB message type " + msgType);
 			}
-
+			rfb.printNumBytesRead();
+			bufSize = rfb.getNumBytesRead() - bufSize;
+			System.out.println("bufSize="+bufSize);
+			
+//			rfb.bufResetSend((int)bufSize);
+			if(rfb.available() < bufSize){
+				System.out.println("rfb.available() < bufSize");
+			
+			}
+						
 		}
 	}
 
--- a/src/VncProxyService.java	Sun Apr 17 02:16:52 2011 +0900
+++ b/src/VncProxyService.java	Sun Apr 17 19:33:01 2011 +0900
@@ -11,9 +11,8 @@
 
 		v.init();
 //		v.start();
-		v.run();
 	}
-
+	
 	String[] mainArgs;
 
 	// RfbProto rfb;