Mercurial > hg > Members > nobuyasu > tightVNCProxy
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"); + + } + } }