# HG changeset patch # User e085711 # Date 1302649253 -32400 # Node ID 60a87e27753629da11f74cfc53e624ed5f5bc40e # Parent c930d146670f4abab4c267528d947ce0b19ce807 indent diff -r c930d146670f -r 60a87e277536 src/InStream.java --- a/src/InStream.java Wed Apr 13 07:48:49 2011 +0900 +++ b/src/InStream.java Wed Apr 13 08:00:53 2011 +0900 @@ -23,155 +23,178 @@ abstract public class InStream { - // check() ensures there is buffer data for at least one item of size - // itemSize bytes. Returns the number of items in the buffer (up to a - // maximum of nItems). + // check() ensures there is buffer data for at least one item of size + // itemSize bytes. Returns the number of items in the buffer (up to a + // maximum of nItems). + + public final int check(int itemSize, int nItems) throws Exception { + if (ptr + itemSize * nItems > end) { + if (ptr + itemSize > end) + return overrun(itemSize, nItems); - public final int check(int itemSize, int nItems) throws Exception { - if (ptr + itemSize * nItems > end) { - if (ptr + itemSize > end) - return overrun(itemSize, nItems); + nItems = (end - ptr) / itemSize; + } + return nItems; + } - nItems = (end - ptr) / itemSize; - } - return nItems; - } + public final void check(int itemSize) throws Exception { + if (ptr + itemSize > end) + overrun(itemSize, 1); + } + + // readU/SN() methods read unsigned and signed N-bit integers. - public final void check(int itemSize) throws Exception { - if (ptr + itemSize > end) - overrun(itemSize, 1); - } - - // readU/SN() methods read unsigned and signed N-bit integers. + public final int readS8() throws Exception { + check(1); + return b[ptr++]; + } - public final int readS8() throws Exception { - check(1); return b[ptr++]; - } + public final int readS16() throws Exception { + check(2); + int b0 = b[ptr++]; + int b1 = b[ptr++] & 0xff; + return b0 << 8 | b1; + } - public final int readS16() throws Exception { - check(2); int b0 = b[ptr++]; - int b1 = b[ptr++] & 0xff; return b0 << 8 | b1; - } + public final int readS32() throws Exception { + check(4); + int b0 = b[ptr++]; + int b1 = b[ptr++] & 0xff; + int b2 = b[ptr++] & 0xff; + int b3 = b[ptr++] & 0xff; + return b0 << 24 | b1 << 16 | b2 << 8 | b3; + } - public final int readS32() throws Exception { - check(4); int b0 = b[ptr++]; - int b1 = b[ptr++] & 0xff; - int b2 = b[ptr++] & 0xff; - int b3 = b[ptr++] & 0xff; - return b0 << 24 | b1 << 16 | b2 << 8 | b3; - } + public final int readU8() throws Exception { + return readS8() & 0xff; + } + + public final int readU16() throws Exception { + return readS16() & 0xffff; + } - public final int readU8() throws Exception { - return readS8() & 0xff; - } + public final int readU32() throws Exception { + return readS32() & 0xffffffff; + } - public final int readU16() throws Exception { - return readS16() & 0xffff; - } + // readString() reads a string - a U32 length followed by the data. - public final int readU32() throws Exception { - return readS32() & 0xffffffff; - } - - // readString() reads a string - a U32 length followed by the data. + public final String readString() throws Exception { + int len = readU32(); + if (len > maxStringLength) + throw new Exception("InStream max string length exceeded"); - public final String readString() throws Exception { - int len = readU32(); - if (len > maxStringLength) - throw new Exception("InStream max string length exceeded"); + char[] str = new char[len]; + int i = 0; + while (i < len) { + int j = i + check(1, len - i); + while (i < j) { + str[i++] = (char) b[ptr++]; + } + } + + return new String(str); + } - char[] str = new char[len]; - int i = 0; - while (i < len) { - int j = i + check(1, len - i); - while (i < j) { - str[i++] = (char)b[ptr++]; - } - } + // maxStringLength protects against allocating a huge buffer. Set it + // higher if you need longer strings. + + public static int maxStringLength = 65535; - return new String(str); - } + public final void skip(int bytes) throws Exception { + while (bytes > 0) { + int n = check(1, bytes); + ptr += n; + bytes -= n; + } + } - // maxStringLength protects against allocating a huge buffer. Set it - // higher if you need longer strings. - - public static int maxStringLength = 65535; + // readBytes() reads an exact number of bytes into an array at an offset. - public final void skip(int bytes) throws Exception { - while (bytes > 0) { - int n = check(1, bytes); - ptr += n; - bytes -= n; - } - } + public void readBytes(byte[] data, int offset, int length) throws Exception { + int offsetEnd = offset + length; + while (offset < offsetEnd) { + int n = check(1, offsetEnd - offset); + System.arraycopy(b, ptr, data, offset, n); + ptr += n; + offset += n; + } + } - // readBytes() reads an exact number of bytes into an array at an offset. + // readOpaqueN() reads a quantity "without byte-swapping". Because java has + // no byte-ordering, we just use big-endian. + + public final int readOpaque8() throws Exception { + return readU8(); + } + + public final int readOpaque16() throws Exception { + return readU16(); + } - public void readBytes(byte[] data, int offset, int length) throws Exception { - int offsetEnd = offset + length; - while (offset < offsetEnd) { - int n = check(1, offsetEnd - offset); - System.arraycopy(b, ptr, data, offset, n); - ptr += n; - offset += n; - } - } + public final int readOpaque32() throws Exception { + return readU32(); + } - // readOpaqueN() reads a quantity "without byte-swapping". Because java has - // no byte-ordering, we just use big-endian. - - public final int readOpaque8() throws Exception { - return readU8(); - } + public final int readOpaque24A() throws Exception { + check(3); + int b0 = b[ptr++]; + int b1 = b[ptr++]; + int b2 = b[ptr++]; + return b0 << 24 | b1 << 16 | b2 << 8; + } - public final int readOpaque16() throws Exception { - return readU16(); - } + public final int readOpaque24B() throws Exception { + check(3); + int b0 = b[ptr++]; + int b1 = b[ptr++]; + int b2 = b[ptr++]; + return b0 << 16 | b1 << 8 | b2; + } - public final int readOpaque32() throws Exception { - return readU32(); - } + // pos() returns the position in the stream. - public final int readOpaque24A() throws Exception { - check(3); int b0 = b[ptr++]; - int b1 = b[ptr++]; int b2 = b[ptr++]; - return b0 << 24 | b1 << 16 | b2 << 8; - } + abstract public int pos(); + + // bytesAvailable() returns true if at least one byte can be read from the + // stream without blocking. i.e. if false is returned then readU8() would + // block. + + public boolean bytesAvailable() { + return end != ptr; + } - public final int readOpaque24B() throws Exception { - check(3); int b0 = b[ptr++]; - int b1 = b[ptr++]; int b2 = b[ptr++]; - return b0 << 16 | b1 << 8 | b2; - } - - // pos() returns the position in the stream. + // getbuf(), getptr(), getend() and setptr() are "dirty" methods which allow + // you to manipulate the buffer directly. This is useful for a stream which + // is a wrapper around an underlying stream. - abstract public int pos(); + public final byte[] getbuf() { + return b; + } - // bytesAvailable() returns true if at least one byte can be read from the - // stream without blocking. i.e. if false is returned then readU8() would - // block. - - public boolean bytesAvailable() { return end != ptr; } + public final int getptr() { + return ptr; + } - // getbuf(), getptr(), getend() and setptr() are "dirty" methods which allow - // you to manipulate the buffer directly. This is useful for a stream which - // is a wrapper around an underlying stream. + public final int getend() { + return end; + } - public final byte[] getbuf() { return b; } - public final int getptr() { return ptr; } - public final int getend() { return end; } - public final void setptr(int p) { ptr = p; } + public final void setptr(int p) { + ptr = p; + } - // overrun() is implemented by a derived class to cope with buffer overrun. - // It ensures there are at least itemSize bytes of buffer data. Returns - // the number of items in the buffer (up to a maximum of nItems). itemSize - // is supposed to be "small" (a few bytes). + // overrun() is implemented by a derived class to cope with buffer overrun. + // It ensures there are at least itemSize bytes of buffer data. Returns + // the number of items in the buffer (up to a maximum of nItems). itemSize + // is supposed to be "small" (a few bytes). + + abstract protected int overrun(int itemSize, int nItems) throws Exception; - abstract protected int overrun(int itemSize, int nItems) throws Exception; + protected InStream() { + } - protected InStream() {} - protected byte[] b; - protected int ptr; - protected int end; + protected byte[] b; + protected int ptr; + protected int end; } diff -r c930d146670f -r 60a87e277536 src/RfbProto.java --- a/src/RfbProto.java Wed Apr 13 07:48:49 2011 +0900 +++ b/src/RfbProto.java Wed Apr 13 08:00:53 2011 +0900 @@ -32,1349 +32,1326 @@ class RfbProto { - final static String - versionMsg_3_3 = "RFB 003.003\n", - versionMsg_3_7 = "RFB 003.007\n", - versionMsg_3_8 = "RFB 003.008\n"; + final static String versionMsg_3_3 = "RFB 003.003\n", + versionMsg_3_7 = "RFB 003.007\n", versionMsg_3_8 = "RFB 003.008\n"; + + // Vendor signatures: standard VNC/RealVNC, TridiaVNC, and TightVNC + final static String StandardVendor = "STDV", TridiaVncVendor = "TRDV", + TightVncVendor = "TGHT"; - // Vendor signatures: standard VNC/RealVNC, TridiaVNC, and TightVNC - final static String - StandardVendor = "STDV", - TridiaVncVendor = "TRDV", - TightVncVendor = "TGHT"; + // Security types + final static int SecTypeInvalid = 0, SecTypeNone = 1, SecTypeVncAuth = 2, + SecTypeTight = 16; + + // Supported tunneling types + final static int NoTunneling = 0; + final static String SigNoTunneling = "NOTUNNEL"; - // Security types - final static int - SecTypeInvalid = 0, - SecTypeNone = 1, - SecTypeVncAuth = 2, - SecTypeTight = 16; + // Supported authentication types + final static int AuthNone = 1, AuthVNC = 2, AuthUnixLogin = 129; + final static String SigAuthNone = "NOAUTH__", SigAuthVNC = "VNCAUTH_", + SigAuthUnixLogin = "ULGNAUTH"; + + // VNC authentication results + final static int VncAuthOK = 0, VncAuthFailed = 1, VncAuthTooMany = 2; - // Supported tunneling types - final static int - NoTunneling = 0; - final static String - SigNoTunneling = "NOTUNNEL"; + // Standard server-to-client messages + final static int FramebufferUpdate = 0, SetColourMapEntries = 1, Bell = 2, + ServerCutText = 3; - // Supported authentication types - final static int - AuthNone = 1, - AuthVNC = 2, - AuthUnixLogin = 129; - final static String - SigAuthNone = "NOAUTH__", - SigAuthVNC = "VNCAUTH_", - SigAuthUnixLogin = "ULGNAUTH"; + // Non-standard server-to-client messages + final static int EndOfContinuousUpdates = 150; + final static String SigEndOfContinuousUpdates = "CUS_EOCU"; - // VNC authentication results - final static int - VncAuthOK = 0, - VncAuthFailed = 1, - VncAuthTooMany = 2; + // Standard client-to-server messages + final static int SetPixelFormat = 0, FixColourMapEntries = 1, + SetEncodings = 2, FramebufferUpdateRequest = 3, KeyboardEvent = 4, + PointerEvent = 5, ClientCutText = 6; - // Standard server-to-client messages - final static int - FramebufferUpdate = 0, - SetColourMapEntries = 1, - Bell = 2, - ServerCutText = 3; + // Non-standard client-to-server messages + final static int EnableContinuousUpdates = 150; + final static String SigEnableContinuousUpdates = "CUC_ENCU"; - // Non-standard server-to-client messages - final static int - EndOfContinuousUpdates = 150; - final static String - SigEndOfContinuousUpdates = "CUS_EOCU"; + // Supported encodings and pseudo-encodings + final static int EncodingRaw = 0, EncodingCopyRect = 1, EncodingRRE = 2, + EncodingCoRRE = 4, EncodingHextile = 5, EncodingZlib = 6, + EncodingTight = 7, EncodingZRLE = 16, + EncodingCompressLevel0 = 0xFFFFFF00, + EncodingQualityLevel0 = 0xFFFFFFE0, EncodingXCursor = 0xFFFFFF10, + EncodingRichCursor = 0xFFFFFF11, EncodingPointerPos = 0xFFFFFF18, + EncodingLastRect = 0xFFFFFF20, EncodingNewFBSize = 0xFFFFFF21; + final static String SigEncodingRaw = "RAW_____", + SigEncodingCopyRect = "COPYRECT", SigEncodingRRE = "RRE_____", + SigEncodingCoRRE = "CORRE___", SigEncodingHextile = "HEXTILE_", + SigEncodingZlib = "ZLIB____", SigEncodingTight = "TIGHT___", + SigEncodingZRLE = "ZRLE____", + SigEncodingCompressLevel0 = "COMPRLVL", + SigEncodingQualityLevel0 = "JPEGQLVL", + SigEncodingXCursor = "X11CURSR", + SigEncodingRichCursor = "RCHCURSR", + SigEncodingPointerPos = "POINTPOS", + SigEncodingLastRect = "LASTRECT", + SigEncodingNewFBSize = "NEWFBSIZ"; - // Standard client-to-server messages - final static int - SetPixelFormat = 0, - FixColourMapEntries = 1, - SetEncodings = 2, - FramebufferUpdateRequest = 3, - KeyboardEvent = 4, - PointerEvent = 5, - ClientCutText = 6; + final static int MaxNormalEncoding = 255; - // Non-standard client-to-server messages - final static int - EnableContinuousUpdates = 150; - final static String - SigEnableContinuousUpdates = "CUC_ENCU"; + // Contstants used in the Hextile decoder + final static int HextileRaw = 1, HextileBackgroundSpecified = 2, + HextileForegroundSpecified = 4, HextileAnySubrects = 8, + HextileSubrectsColoured = 16; - // Supported encodings and pseudo-encodings - final static int - EncodingRaw = 0, - EncodingCopyRect = 1, - EncodingRRE = 2, - EncodingCoRRE = 4, - EncodingHextile = 5, - EncodingZlib = 6, - EncodingTight = 7, - EncodingZRLE = 16, - EncodingCompressLevel0 = 0xFFFFFF00, - EncodingQualityLevel0 = 0xFFFFFFE0, - EncodingXCursor = 0xFFFFFF10, - EncodingRichCursor = 0xFFFFFF11, - EncodingPointerPos = 0xFFFFFF18, - EncodingLastRect = 0xFFFFFF20, - EncodingNewFBSize = 0xFFFFFF21; - final static String - SigEncodingRaw = "RAW_____", - SigEncodingCopyRect = "COPYRECT", - SigEncodingRRE = "RRE_____", - SigEncodingCoRRE = "CORRE___", - SigEncodingHextile = "HEXTILE_", - SigEncodingZlib = "ZLIB____", - SigEncodingTight = "TIGHT___", - SigEncodingZRLE = "ZRLE____", - SigEncodingCompressLevel0 = "COMPRLVL", - SigEncodingQualityLevel0 = "JPEGQLVL", - SigEncodingXCursor = "X11CURSR", - SigEncodingRichCursor = "RCHCURSR", - SigEncodingPointerPos = "POINTPOS", - SigEncodingLastRect = "LASTRECT", - SigEncodingNewFBSize = "NEWFBSIZ"; + // Contstants used in the Tight decoder + final static int TightMinToCompress = 12; + final static int TightExplicitFilter = 0x04, TightFill = 0x08, + TightJpeg = 0x09, TightMaxSubencoding = 0x09, + TightFilterCopy = 0x00, TightFilterPalette = 0x01, + TightFilterGradient = 0x02; + + String host; + int port; + Socket sock; + OutputStream os; + SessionRecorder rec; + boolean inNormalProtocol = false; + VncViewer viewer; - final static int MaxNormalEncoding = 255; + // Input stream is declared private to make sure it can be accessed + // only via RfbProto methods. We have to do this because we want to + // count how many bytes were read. + private DataInputStream is; + private long numBytesRead = 0; + + public long getNumBytesRead() { + return numBytesRead; + } - // Contstants used in the Hextile decoder - final static int - HextileRaw = 1, - HextileBackgroundSpecified = 2, - HextileForegroundSpecified = 4, - HextileAnySubrects = 8, - HextileSubrectsColoured = 16; + // Java on UNIX does not call keyPressed() on some keys, for example + // swedish keys To prevent our workaround to produce duplicate + // keypresses on JVMs that actually works, keep track of if + // keyPressed() for a "broken" key was called or not. + boolean brokenKeyPressed = false; + + // This will be set to true on the first framebuffer update + // containing Zlib-, ZRLE- or Tight-encoded data. + boolean wereZlibUpdates = false; + + // This will be set to false if the startSession() was called after + // we have received at least one Zlib-, ZRLE- or Tight-encoded + // framebuffer update. + boolean recordFromBeginning = true; - // Contstants used in the Tight decoder - final static int TightMinToCompress = 12; - final static int - TightExplicitFilter = 0x04, - TightFill = 0x08, - TightJpeg = 0x09, - TightMaxSubencoding = 0x09, - TightFilterCopy = 0x00, - TightFilterPalette = 0x01, - TightFilterGradient = 0x02; + // This fields are needed to show warnings about inefficiently saved + // sessions only once per each saved session file. + boolean zlibWarningShown; + boolean tightWarningShown; + // Before starting to record each saved session, we set this field + // to 0, and increment on each framebuffer update. We don't flush + // the SessionRecorder data into the file before the second update. + // This allows us to write initial framebuffer update with zero + // timestamp, to let the player show initial desktop before + // playback. + int numUpdatesInSession; - String host; - int port; - Socket sock; - OutputStream os; - SessionRecorder rec; - boolean inNormalProtocol = false; - VncViewer viewer; + // Measuring network throughput. + boolean timing; + long timeWaitedIn100us; + long timedKbits; - // Input stream is declared private to make sure it can be accessed - // only via RfbProto methods. We have to do this because we want to - // count how many bytes were read. - private DataInputStream is; - private long numBytesRead = 0; - public long getNumBytesRead() { return numBytesRead; } - - + // Protocol version and TightVNC-specific protocol options. + int serverMajor, serverMinor; + int clientMajor, clientMinor; + boolean protocolTightVNC; + CapsContainer tunnelCaps, authCaps; + CapsContainer serverMsgCaps, clientMsgCaps; + CapsContainer encodingCaps; + + // If true, informs that the RFB socket was closed. + private boolean closed; - // Java on UNIX does not call keyPressed() on some keys, for example - // swedish keys To prevent our workaround to produce duplicate - // keypresses on JVMs that actually works, keep track of if - // keyPressed() for a "broken" key was called or not. - boolean brokenKeyPressed = false; - - // This will be set to true on the first framebuffer update - // containing Zlib-, ZRLE- or Tight-encoded data. - boolean wereZlibUpdates = false; + // + // Constructor. Make TCP connection to RFB server. + // - // This will be set to false if the startSession() was called after - // we have received at least one Zlib-, ZRLE- or Tight-encoded - // framebuffer update. - boolean recordFromBeginning = true; + RfbProto(String h, int p, VncViewer v) throws IOException { + viewer = v; + host = h; + port = p; - // This fields are needed to show warnings about inefficiently saved - // sessions only once per each saved session file. - boolean zlibWarningShown; - boolean tightWarningShown; + if (viewer.socketFactory == null) { + sock = new Socket(host, port); - // Before starting to record each saved session, we set this field - // to 0, and increment on each framebuffer update. We don't flush - // the SessionRecorder data into the file before the second update. - // This allows us to write initial framebuffer update with zero - // timestamp, to let the player show initial desktop before - // playback. - int numUpdatesInSession; - - // Measuring network throughput. - boolean timing; - long timeWaitedIn100us; - long timedKbits; + } else { + try { + Class factoryClass = Class.forName(viewer.socketFactory); + SocketFactory factory = (SocketFactory) factoryClass + .newInstance(); + if (viewer.inAnApplet) + sock = factory.createSocket(host, port, viewer); + else + sock = factory.createSocket(host, port, viewer.mainArgs); + } catch (Exception e) { + e.printStackTrace(); + throw new IOException(e.getMessage()); + } + } + is = new DataInputStream(new BufferedInputStream(sock.getInputStream(), + 16384)); + os = sock.getOutputStream(); - // Protocol version and TightVNC-specific protocol options. - int serverMajor, serverMinor; - int clientMajor, clientMinor; - boolean protocolTightVNC; - CapsContainer tunnelCaps, authCaps; - CapsContainer serverMsgCaps, clientMsgCaps; - CapsContainer encodingCaps; + timing = false; + timeWaitedIn100us = 5; + timedKbits = 0; + } - // If true, informs that the RFB socket was closed. - private boolean closed; + synchronized void close() { + try { + sock.close(); + closed = true; + System.out.println("RFB socket closed"); + if (rec != null) { + rec.close(); + rec = null; + } + } catch (Exception e) { + e.printStackTrace(); + } + } - // - // Constructor. Make TCP connection to RFB server. - // - - RfbProto(String h, int p, VncViewer v) throws IOException { - viewer = v; - host = h; - port = p; + synchronized boolean closed() { + return closed; + } - if (viewer.socketFactory == null) { - sock = new Socket(host, port); - - } else { - try { - Class factoryClass = Class.forName(viewer.socketFactory); - SocketFactory factory = (SocketFactory)factoryClass.newInstance(); - if (viewer.inAnApplet) - sock = factory.createSocket(host, port, viewer); - else - sock = factory.createSocket(host, port, viewer.mainArgs); - } catch(Exception e) { - e.printStackTrace(); - throw new IOException(e.getMessage()); - } - } - is = new DataInputStream(new BufferedInputStream(sock.getInputStream(), - 16384)); - os = sock.getOutputStream(); + // + // Read server's protocol version message + // + + void readVersionMsg() throws Exception { + + byte[] b = new byte[12]; + + readFully(b); - timing = false; - timeWaitedIn100us = 5; - timedKbits = 0; - } + if ((b[0] != 'R') || (b[1] != 'F') || (b[2] != 'B') || (b[3] != ' ') + || (b[4] < '0') || (b[4] > '9') || (b[5] < '0') || (b[5] > '9') + || (b[6] < '0') || (b[6] > '9') || (b[7] != '.') + || (b[8] < '0') || (b[8] > '9') || (b[9] < '0') || (b[9] > '9') + || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n')) { + throw new Exception("Host " + host + " port " + port + + " is not an RFB server"); + } + serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0'); + serverMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0'); - synchronized void close() { - try { - sock.close(); - closed = true; - System.out.println("RFB socket closed"); - if (rec != null) { - rec.close(); - rec = null; - } - } catch (Exception e) { - e.printStackTrace(); - } - } + if (serverMajor < 3) { + throw new Exception( + "RFB server does not support protocol version 3"); + } + } + + // + // Write our protocol version message + // - synchronized boolean closed() { - return closed; - } - - // - // Read server's protocol version message - // - - void readVersionMsg() throws Exception { - - byte[] b = new byte[12]; + void writeVersionMsg() throws IOException { + clientMajor = 3; + if (serverMajor > 3 || serverMinor >= 8) { + clientMinor = 8; + os.write(versionMsg_3_8.getBytes()); + } else if (serverMinor >= 7) { + clientMinor = 7; + os.write(versionMsg_3_7.getBytes()); + } else { + clientMinor = 3; + os.write(versionMsg_3_3.getBytes()); + } + protocolTightVNC = false; + initCapabilities(); + } - readFully(b); + // + // Negotiate the authentication scheme. + // - if ((b[0] != 'R') || (b[1] != 'F') || (b[2] != 'B') || (b[3] != ' ') - || (b[4] < '0') || (b[4] > '9') || (b[5] < '0') || (b[5] > '9') - || (b[6] < '0') || (b[6] > '9') || (b[7] != '.') - || (b[8] < '0') || (b[8] > '9') || (b[9] < '0') || (b[9] > '9') - || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n')) - { - throw new Exception("Host " + host + " port " + port + - " is not an RFB server"); - } + int negotiateSecurity() throws Exception { + return (clientMinor >= 7) ? selectSecurityType() : readSecurityType(); + } - serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0'); - serverMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0'); + // + // Read security type from the server (protocol version 3.3). + // - if (serverMajor < 3) { - throw new Exception("RFB server does not support protocol version 3"); - } - } - + int readSecurityType() throws Exception { + int secType = readU32(); - // - // Write our protocol version message - // + switch (secType) { + case SecTypeInvalid: + readConnFailedReason(); + return SecTypeInvalid; // should never be executed + case SecTypeNone: + case SecTypeVncAuth: + return secType; + default: + throw new Exception("Unknown security type from RFB server: " + + secType); + } + } - void writeVersionMsg() throws IOException { - clientMajor = 3; - if (serverMajor > 3 || serverMinor >= 8) { - clientMinor = 8; - os.write(versionMsg_3_8.getBytes()); - } else if (serverMinor >= 7) { - clientMinor = 7; - os.write(versionMsg_3_7.getBytes()); - } else { - clientMinor = 3; - os.write(versionMsg_3_3.getBytes()); - } - protocolTightVNC = false; - initCapabilities(); - } + // + // Select security type from the server's list (protocol versions 3.7/3.8). + // + + int selectSecurityType() throws Exception { + int secType = SecTypeInvalid; + // Read the list of secutiry types. + int nSecTypes = readU8(); + if (nSecTypes == 0) { + readConnFailedReason(); + return SecTypeInvalid; // should never be executed + } + byte[] secTypes = new byte[nSecTypes]; + readFully(secTypes); - // - // Negotiate the authentication scheme. - // + // Find out if the server supports TightVNC protocol extensions + for (int i = 0; i < nSecTypes; i++) { + if (secTypes[i] == SecTypeTight) { + protocolTightVNC = true; + os.write(SecTypeTight); + return SecTypeTight; + } + } - int negotiateSecurity() throws Exception { - return (clientMinor >= 7) ? - selectSecurityType() : readSecurityType(); - } + // Find first supported security type. + for (int i = 0; i < nSecTypes; i++) { + if (secTypes[i] == SecTypeNone || secTypes[i] == SecTypeVncAuth) { + secType = secTypes[i]; + break; + } + } - // - // Read security type from the server (protocol version 3.3). - // - - int readSecurityType() throws Exception { - int secType = readU32(); + if (secType == SecTypeInvalid) { + throw new Exception("Server did not offer supported security type"); + } else { + os.write(secType); + } - switch (secType) { - case SecTypeInvalid: - readConnFailedReason(); - return SecTypeInvalid; // should never be executed - case SecTypeNone: - case SecTypeVncAuth: - return secType; - default: - throw new Exception("Unknown security type from RFB server: " + secType); - } - } + return secType; + } + + // + // Perform "no authentication". + // - // - // Select security type from the server's list (protocol versions 3.7/3.8). - // - - int selectSecurityType() throws Exception { - int secType = SecTypeInvalid; + void authenticateNone() throws Exception { + if (clientMinor >= 8) + readSecurityResult("No authentication"); + } - // Read the list of secutiry types. - int nSecTypes = readU8(); - if (nSecTypes == 0) { - readConnFailedReason(); - return SecTypeInvalid; // should never be executed - } - byte[] secTypes = new byte[nSecTypes]; - readFully(secTypes); + // + // Perform standard VNC Authentication. + // + + void authenticateVNC(String pw) throws Exception { + byte[] challenge = new byte[16]; + readFully(challenge); + + if (pw.length() > 8) + pw = pw.substring(0, 8); // Truncate to 8 chars - // Find out if the server supports TightVNC protocol extensions - for (int i = 0; i < nSecTypes; i++) { - if (secTypes[i] == SecTypeTight) { - protocolTightVNC = true; - os.write(SecTypeTight); - return SecTypeTight; - } - } + // Truncate password on the first zero byte. + int firstZero = pw.indexOf(0); + if (firstZero != -1) + pw = pw.substring(0, firstZero); - // Find first supported security type. - for (int i = 0; i < nSecTypes; i++) { - if (secTypes[i] == SecTypeNone || secTypes[i] == SecTypeVncAuth) { - secType = secTypes[i]; - break; - } - } + byte[] key = { 0, 0, 0, 0, 0, 0, 0, 0 }; + System.arraycopy(pw.getBytes(), 0, key, 0, pw.length()); + + DesCipher des = new DesCipher(key); + + des.encrypt(challenge, 0, challenge, 0); + des.encrypt(challenge, 8, challenge, 8); - if (secType == SecTypeInvalid) { - throw new Exception("Server did not offer supported security type"); - } else { - os.write(secType); - } + os.write(challenge); - return secType; - } + readSecurityResult("VNC authentication"); + } - // - // Perform "no authentication". - // + // + // Read security result. + // Throws an exception on authentication failure. + // - void authenticateNone() throws Exception { - if (clientMinor >= 8) - readSecurityResult("No authentication"); - } + void readSecurityResult(String authType) throws Exception { + int securityResult = readU32(); - // - // Perform standard VNC Authentication. - // - - void authenticateVNC(String pw) throws Exception { - byte[] challenge = new byte[16]; - readFully(challenge); + switch (securityResult) { + case VncAuthOK: + System.out.println(authType + ": success"); + break; + case VncAuthFailed: + if (clientMinor >= 8) + readConnFailedReason(); + throw new Exception(authType + ": failed"); + case VncAuthTooMany: + throw new Exception(authType + ": failed, too many tries"); + default: + throw new Exception(authType + ": unknown result " + securityResult); + } + } - if (pw.length() > 8) - pw = pw.substring(0, 8); // Truncate to 8 chars - - // Truncate password on the first zero byte. - int firstZero = pw.indexOf(0); - if (firstZero != -1) - pw = pw.substring(0, firstZero); + // + // Read the string describing the reason for a connection failure, + // and throw an exception. + // - byte[] key = {0, 0, 0, 0, 0, 0, 0, 0}; - System.arraycopy(pw.getBytes(), 0, key, 0, pw.length()); - - DesCipher des = new DesCipher(key); + void readConnFailedReason() throws Exception { + int reasonLen = readU32(); + byte[] reason = new byte[reasonLen]; + readFully(reason); + throw new Exception(new String(reason)); + } - des.encrypt(challenge, 0, challenge, 0); - des.encrypt(challenge, 8, challenge, 8); - - os.write(challenge); + // + // Initialize capability lists (TightVNC protocol extensions). + // - readSecurityResult("VNC authentication"); - } + void initCapabilities() { + tunnelCaps = new CapsContainer(); + authCaps = new CapsContainer(); + serverMsgCaps = new CapsContainer(); + clientMsgCaps = new CapsContainer(); + encodingCaps = new CapsContainer(); - // - // Read security result. - // Throws an exception on authentication failure. - // + // Supported authentication methods + authCaps.add(AuthNone, StandardVendor, SigAuthNone, "No authentication"); + authCaps.add(AuthVNC, StandardVendor, SigAuthVNC, + "Standard VNC password authentication"); - void readSecurityResult(String authType) throws Exception { - int securityResult = readU32(); + // Supported non-standard server-to-client messages + // [NONE] + + // Supported non-standard client-to-server messages + // [NONE] - switch (securityResult) { - case VncAuthOK: - System.out.println(authType + ": success"); - break; - case VncAuthFailed: - if (clientMinor >= 8) - readConnFailedReason(); - throw new Exception(authType + ": failed"); - case VncAuthTooMany: - throw new Exception(authType + ": failed, too many tries"); - default: - throw new Exception(authType + ": unknown result " + securityResult); - } - } - - // - // Read the string describing the reason for a connection failure, - // and throw an exception. - // + // Supported encoding types + encodingCaps.add(EncodingCopyRect, StandardVendor, SigEncodingCopyRect, + "Standard CopyRect encoding"); + encodingCaps.add(EncodingRRE, StandardVendor, SigEncodingRRE, + "Standard RRE encoding"); + encodingCaps.add(EncodingCoRRE, StandardVendor, SigEncodingCoRRE, + "Standard CoRRE encoding"); + encodingCaps.add(EncodingHextile, StandardVendor, SigEncodingHextile, + "Standard Hextile encoding"); + encodingCaps.add(EncodingZRLE, StandardVendor, SigEncodingZRLE, + "Standard ZRLE encoding"); + encodingCaps.add(EncodingZlib, TridiaVncVendor, SigEncodingZlib, + "Zlib encoding"); + encodingCaps.add(EncodingTight, TightVncVendor, SigEncodingTight, + "Tight encoding"); - void readConnFailedReason() throws Exception { - int reasonLen = readU32(); - byte[] reason = new byte[reasonLen]; - readFully(reason); - throw new Exception(new String(reason)); - } - - // - // Initialize capability lists (TightVNC protocol extensions). - // + // Supported pseudo-encoding types + encodingCaps.add(EncodingCompressLevel0, TightVncVendor, + SigEncodingCompressLevel0, "Compression level"); + encodingCaps.add(EncodingQualityLevel0, TightVncVendor, + SigEncodingQualityLevel0, "JPEG quality level"); + encodingCaps.add(EncodingXCursor, TightVncVendor, SigEncodingXCursor, + "X-style cursor shape update"); + encodingCaps.add(EncodingRichCursor, TightVncVendor, + SigEncodingRichCursor, "Rich-color cursor shape update"); + encodingCaps.add(EncodingPointerPos, TightVncVendor, + SigEncodingPointerPos, "Pointer position update"); + encodingCaps.add(EncodingLastRect, TightVncVendor, SigEncodingLastRect, + "LastRect protocol extension"); + encodingCaps.add(EncodingNewFBSize, TightVncVendor, + SigEncodingNewFBSize, "Framebuffer size change"); + } - void initCapabilities() { - tunnelCaps = new CapsContainer(); - authCaps = new CapsContainer(); - serverMsgCaps = new CapsContainer(); - clientMsgCaps = new CapsContainer(); - encodingCaps = new CapsContainer(); + // + // Setup tunneling (TightVNC protocol extensions) + // - // Supported authentication methods - authCaps.add(AuthNone, StandardVendor, SigAuthNone, - "No authentication"); - authCaps.add(AuthVNC, StandardVendor, SigAuthVNC, - "Standard VNC password authentication"); - - // Supported non-standard server-to-client messages - // [NONE] + void setupTunneling() throws IOException { + int nTunnelTypes = readU32(); + if (nTunnelTypes != 0) { + readCapabilityList(tunnelCaps, nTunnelTypes); - // Supported non-standard client-to-server messages - // [NONE] + // We don't support tunneling yet. + writeInt(NoTunneling); + } + } - // Supported encoding types - encodingCaps.add(EncodingCopyRect, StandardVendor, - SigEncodingCopyRect, "Standard CopyRect encoding"); - encodingCaps.add(EncodingRRE, StandardVendor, - SigEncodingRRE, "Standard RRE encoding"); - encodingCaps.add(EncodingCoRRE, StandardVendor, - SigEncodingCoRRE, "Standard CoRRE encoding"); - encodingCaps.add(EncodingHextile, StandardVendor, - SigEncodingHextile, "Standard Hextile encoding"); - encodingCaps.add(EncodingZRLE, StandardVendor, - SigEncodingZRLE, "Standard ZRLE encoding"); - encodingCaps.add(EncodingZlib, TridiaVncVendor, - SigEncodingZlib, "Zlib encoding"); - encodingCaps.add(EncodingTight, TightVncVendor, - SigEncodingTight, "Tight encoding"); + // + // Negotiate authentication scheme (TightVNC protocol extensions) + // + + int negotiateAuthenticationTight() throws Exception { + int nAuthTypes = readU32(); + if (nAuthTypes == 0) + return AuthNone; - // Supported pseudo-encoding types - encodingCaps.add(EncodingCompressLevel0, TightVncVendor, - SigEncodingCompressLevel0, "Compression level"); - encodingCaps.add(EncodingQualityLevel0, TightVncVendor, - SigEncodingQualityLevel0, "JPEG quality level"); - encodingCaps.add(EncodingXCursor, TightVncVendor, - SigEncodingXCursor, "X-style cursor shape update"); - encodingCaps.add(EncodingRichCursor, TightVncVendor, - SigEncodingRichCursor, "Rich-color cursor shape update"); - encodingCaps.add(EncodingPointerPos, TightVncVendor, - SigEncodingPointerPos, "Pointer position update"); - encodingCaps.add(EncodingLastRect, TightVncVendor, - SigEncodingLastRect, "LastRect protocol extension"); - encodingCaps.add(EncodingNewFBSize, TightVncVendor, - SigEncodingNewFBSize, "Framebuffer size change"); - } + readCapabilityList(authCaps, nAuthTypes); + for (int i = 0; i < authCaps.numEnabled(); i++) { + int authType = authCaps.getByOrder(i); + if (authType == AuthNone || authType == AuthVNC) { + writeInt(authType); + return authType; + } + } + throw new Exception("No suitable authentication scheme found"); + } + + // + // Read a capability list (TightVNC protocol extensions) + // - // - // Setup tunneling (TightVNC protocol extensions) - // + void readCapabilityList(CapsContainer caps, int count) throws IOException { + int code; + byte[] vendor = new byte[4]; + byte[] name = new byte[8]; + for (int i = 0; i < count; i++) { + code = readU32(); + readFully(vendor); + readFully(name); + caps.enable(new CapabilityInfo(code, vendor, name)); + } + } - void setupTunneling() throws IOException { - int nTunnelTypes = readU32(); - if (nTunnelTypes != 0) { - readCapabilityList(tunnelCaps, nTunnelTypes); - - // We don't support tunneling yet. - writeInt(NoTunneling); - } - } + // + // Write a 32-bit integer into the output stream. + // - // - // Negotiate authentication scheme (TightVNC protocol extensions) - // + void writeInt(int value) throws IOException { + byte[] b = new byte[4]; + b[0] = (byte) ((value >> 24) & 0xff); + b[1] = (byte) ((value >> 16) & 0xff); + b[2] = (byte) ((value >> 8) & 0xff); + b[3] = (byte) (value & 0xff); + os.write(b); + } - int negotiateAuthenticationTight() throws Exception { - int nAuthTypes = readU32(); - if (nAuthTypes == 0) - return AuthNone; + // + // Write the client initialisation message + // - readCapabilityList(authCaps, nAuthTypes); - for (int i = 0; i < authCaps.numEnabled(); i++) { - int authType = authCaps.getByOrder(i); - if (authType == AuthNone || authType == AuthVNC) { - writeInt(authType); - return authType; - } - } - throw new Exception("No suitable authentication scheme found"); - } + void writeClientInit() throws IOException { + if (viewer.options.shareDesktop) { + os.write(1); + } else { + os.write(0); + } + viewer.options.disableShareDesktop(); + } - // - // Read a capability list (TightVNC protocol extensions) - // + // + // Read the server initialisation message + // - void readCapabilityList(CapsContainer caps, int count) throws IOException { - int code; - byte[] vendor = new byte[4]; - byte[] name = new byte[8]; - for (int i = 0; i < count; i++) { - code = readU32(); - readFully(vendor); - readFully(name); - caps.enable(new CapabilityInfo(code, vendor, name)); - } - } + String desktopName; + int framebufferWidth, framebufferHeight; + int bitsPerPixel, depth; + boolean bigEndian, trueColour; + int redMax, greenMax, blueMax, redShift, greenShift, blueShift; - // - // Write a 32-bit integer into the output stream. - // - - void writeInt(int value) throws IOException { - byte[] b = new byte[4]; - b[0] = (byte) ((value >> 24) & 0xff); - b[1] = (byte) ((value >> 16) & 0xff); - b[2] = (byte) ((value >> 8) & 0xff); - b[3] = (byte) (value & 0xff); - os.write(b); - } - - // - // Write the client initialisation message - // + void readServerInit() throws IOException { + framebufferWidth = readU16(); + framebufferHeight = readU16(); + bitsPerPixel = readU8(); + depth = readU8(); + bigEndian = (readU8() != 0); + trueColour = (readU8() != 0); + redMax = readU16(); + greenMax = readU16(); + blueMax = readU16(); + redShift = readU8(); + greenShift = readU8(); + blueShift = readU8(); + byte[] pad = new byte[3]; + readFully(pad); + int nameLength = readU32(); + byte[] name = new byte[nameLength]; + readFully(name); + desktopName = new String(name); - void writeClientInit() throws IOException { - if (viewer.options.shareDesktop) { - os.write(1); - } else { - os.write(0); - } - viewer.options.disableShareDesktop(); - } + // Read interaction capabilities (TightVNC protocol extensions) + if (protocolTightVNC) { + int nServerMessageTypes = readU16(); + int nClientMessageTypes = readU16(); + int nEncodingTypes = readU16(); + readU16(); + readCapabilityList(serverMsgCaps, nServerMessageTypes); + readCapabilityList(clientMsgCaps, nClientMessageTypes); + readCapabilityList(encodingCaps, nEncodingTypes); + } + inNormalProtocol = true; + } - // - // Read the server initialisation message - // + // + // Create session file and write initial protocol messages into it. + // - String desktopName; - int framebufferWidth, framebufferHeight; - int bitsPerPixel, depth; - boolean bigEndian, trueColour; - int redMax, greenMax, blueMax, redShift, greenShift, blueShift; + void startSession(String fname) throws IOException { + rec = new SessionRecorder(fname); + rec.writeHeader(); + rec.write(versionMsg_3_3.getBytes()); + rec.writeIntBE(SecTypeNone); + rec.writeShortBE(framebufferWidth); + rec.writeShortBE(framebufferHeight); + byte[] fbsServerInitMsg = { 32, 24, 0, 1, 0, (byte) 0xFF, 0, + (byte) 0xFF, 0, (byte) 0xFF, 16, 8, 0, 0, 0, 0 }; + rec.write(fbsServerInitMsg); + rec.writeIntBE(desktopName.length()); + rec.write(desktopName.getBytes()); + numUpdatesInSession = 0; - void readServerInit() throws IOException { - framebufferWidth = readU16(); - framebufferHeight = readU16(); - bitsPerPixel = readU8(); - depth = readU8(); - bigEndian = (readU8() != 0); - trueColour = (readU8() != 0); - redMax = readU16(); - greenMax = readU16(); - blueMax = readU16(); - redShift = readU8(); - greenShift = readU8(); - blueShift = readU8(); - byte[] pad = new byte[3]; - readFully(pad); - int nameLength = readU32(); - byte[] name = new byte[nameLength]; - readFully(name); - desktopName = new String(name); + // FIXME: If there were e.g. ZRLE updates only, that should not + // affect recording of Zlib and Tight updates. So, actually + // we should maintain separate flags for Zlib, ZRLE and + // Tight, instead of one ``wereZlibUpdates'' variable. + // + if (wereZlibUpdates) + recordFromBeginning = false; + + zlibWarningShown = false; + tightWarningShown = false; + } + + // + // Close session file. + // - // Read interaction capabilities (TightVNC protocol extensions) - if (protocolTightVNC) { - int nServerMessageTypes = readU16(); - int nClientMessageTypes = readU16(); - int nEncodingTypes = readU16(); - readU16(); - readCapabilityList(serverMsgCaps, nServerMessageTypes); - readCapabilityList(clientMsgCaps, nClientMessageTypes); - readCapabilityList(encodingCaps, nEncodingTypes); - } + void closeSession() throws IOException { + if (rec != null) { + rec.close(); + rec = null; + } + } + + // + // Set new framebuffer size + // - inNormalProtocol = true; - } - + void setFramebufferSize(int width, int height) { + framebufferWidth = width; + framebufferHeight = height; + } - // - // Create session file and write initial protocol messages into it. - // + // + // Read the server message type + // + + int readServerMessageType() throws IOException { + int msgType = readU8(); - void startSession(String fname) throws IOException { - rec = new SessionRecorder(fname); - rec.writeHeader(); - rec.write(versionMsg_3_3.getBytes()); - rec.writeIntBE(SecTypeNone); - rec.writeShortBE(framebufferWidth); - rec.writeShortBE(framebufferHeight); - byte[] fbsServerInitMsg = { - 32, 24, 0, 1, 0, - (byte)0xFF, 0, (byte)0xFF, 0, (byte)0xFF, - 16, 8, 0, 0, 0, 0 - }; - rec.write(fbsServerInitMsg); - rec.writeIntBE(desktopName.length()); - rec.write(desktopName.getBytes()); - numUpdatesInSession = 0; + // If the session is being recorded: + if (rec != null) { + if (msgType == Bell) { // Save Bell messages in session files. + rec.writeByte(msgType); + if (numUpdatesInSession > 0) + rec.flush(); + } + } - // FIXME: If there were e.g. ZRLE updates only, that should not - // affect recording of Zlib and Tight updates. So, actually - // we should maintain separate flags for Zlib, ZRLE and - // Tight, instead of one ``wereZlibUpdates'' variable. - // - if (wereZlibUpdates) - recordFromBeginning = false; + return msgType; + } - zlibWarningShown = false; - tightWarningShown = false; - } + // + // Read a FramebufferUpdate message + // + + int updateNRects; - // - // Close session file. - // + void readFramebufferUpdate() throws IOException { + skipBytes(1); + updateNRects = readU16(); + // System.out.println(updateNRects); - void closeSession() throws IOException { - if (rec != null) { - rec.close(); - rec = null; - } - } - + // If the session is being recorded: + if (rec != null) { + rec.writeByte(FramebufferUpdate); + rec.writeByte(0); + rec.writeShortBE(updateNRects); + } - // - // Set new framebuffer size - // + numUpdatesInSession++; + } + + // Read a FramebufferUpdate rectangle header + + int updateRectX, updateRectY, updateRectW, updateRectH, updateRectEncoding; - void setFramebufferSize(int width, int height) { - framebufferWidth = width; - framebufferHeight = height; - } - - - // - // Read the server message type - // - - int readServerMessageType() throws IOException { - int msgType = readU8(); + void readFramebufferUpdateRectHdr() throws Exception { + updateRectX = readU16(); + updateRectY = readU16(); + updateRectW = readU16(); + updateRectH = readU16(); + updateRectEncoding = readU32(); + // System.out.println("readU16&32"); - // If the session is being recorded: - if (rec != null) { - if (msgType == Bell) { // Save Bell messages in session files. - rec.writeByte(msgType); - if (numUpdatesInSession > 0) - rec.flush(); - } - } - - return msgType; - } - - - // - // Read a FramebufferUpdate message - // - - int updateNRects; + if (updateRectEncoding == EncodingZlib + || updateRectEncoding == EncodingZRLE + || updateRectEncoding == EncodingTight) + wereZlibUpdates = true; - void readFramebufferUpdate() throws IOException { - skipBytes(1); - updateNRects = readU16(); -// System.out.println(updateNRects); - - // If the session is being recorded: - if (rec != null) { - rec.writeByte(FramebufferUpdate); - rec.writeByte(0); - rec.writeShortBE(updateNRects); - } + // If the session is being recorded: + if (rec != null) { + if (numUpdatesInSession > 1) + rec.flush(); // Flush the output on each rectangle. + rec.writeShortBE(updateRectX); + rec.writeShortBE(updateRectY); + rec.writeShortBE(updateRectW); + rec.writeShortBE(updateRectH); + if (updateRectEncoding == EncodingZlib && !recordFromBeginning) { + // Here we cannot write Zlib-encoded rectangles because the + // decoder won't be able to reproduce zlib stream state. + if (!zlibWarningShown) { + System.out.println("Warning: Raw encoding will be used " + + "instead of Zlib in recorded session."); + zlibWarningShown = true; + } + rec.writeIntBE(EncodingRaw); + } else { + rec.writeIntBE(updateRectEncoding); + if (updateRectEncoding == EncodingTight && !recordFromBeginning + && !tightWarningShown) { + System.out.println("Warning: Re-compressing Tight-encoded " + + "updates for session recording."); + tightWarningShown = true; + } + } + } - numUpdatesInSession++; - } - - // Read a FramebufferUpdate rectangle header - - int updateRectX, updateRectY, updateRectW, updateRectH, updateRectEncoding; + if (updateRectEncoding < 0 || updateRectEncoding > MaxNormalEncoding) + return; - void readFramebufferUpdateRectHdr() throws Exception { - updateRectX = readU16(); - updateRectY = readU16(); - updateRectW = readU16(); - updateRectH = readU16(); - updateRectEncoding = readU32(); -// System.out.println("readU16&32"); + if (updateRectX + updateRectW > framebufferWidth + || updateRectY + updateRectH > framebufferHeight) { + throw new Exception("Framebuffer update rectangle too large: " + + updateRectW + "x" + updateRectH + " at (" + updateRectX + + "," + updateRectY + ")"); + } + } - if (updateRectEncoding == EncodingZlib || - updateRectEncoding == EncodingZRLE || - updateRectEncoding == EncodingTight) - wereZlibUpdates = true; + // Read CopyRect source X and Y. + + int copyRectSrcX, copyRectSrcY; + + void readCopyRect() throws IOException { + copyRectSrcX = readU16(); + copyRectSrcY = readU16(); - // If the session is being recorded: - if (rec != null) { - if (numUpdatesInSession > 1) - rec.flush(); // Flush the output on each rectangle. - rec.writeShortBE(updateRectX); - rec.writeShortBE(updateRectY); - rec.writeShortBE(updateRectW); - rec.writeShortBE(updateRectH); - if (updateRectEncoding == EncodingZlib && !recordFromBeginning) { - // Here we cannot write Zlib-encoded rectangles because the - // decoder won't be able to reproduce zlib stream state. - if (!zlibWarningShown) { - System.out.println("Warning: Raw encoding will be used " + - "instead of Zlib in recorded session."); - zlibWarningShown = true; + // If the session is being recorded: + if (rec != null) { + rec.writeShortBE(copyRectSrcX); + rec.writeShortBE(copyRectSrcY); + } } - rec.writeIntBE(EncodingRaw); - } else { - rec.writeIntBE(updateRectEncoding); - if (updateRectEncoding == EncodingTight && !recordFromBeginning && - !tightWarningShown) { - System.out.println("Warning: Re-compressing Tight-encoded " + - "updates for session recording."); - tightWarningShown = true; + + // + // Read a ServerCutText message + // + + String readServerCutText() throws IOException { + skipBytes(3); + int len = readU32(); + byte[] text = new byte[len]; + readFully(text); + return new String(text); } - } - } + + // + // Read an integer in compact representation (1..3 bytes). + // Such format is used as a part of the Tight encoding. + // Also, this method records data if session recording is active and + // the viewer's recordFromBeginning variable is set to true. + // - if (updateRectEncoding < 0 || updateRectEncoding > MaxNormalEncoding) - return; + int readCompactLen() throws IOException { + int[] portion = new int[3]; + portion[0] = readU8(); + int byteCount = 1; + int len = portion[0] & 0x7F; + if ((portion[0] & 0x80) != 0) { + portion[1] = readU8(); + byteCount++; + len |= (portion[1] & 0x7F) << 7; + if ((portion[1] & 0x80) != 0) { + portion[2] = readU8(); + byteCount++; + len |= (portion[2] & 0xFF) << 14; + } + } - if (updateRectX + updateRectW > framebufferWidth || - updateRectY + updateRectH > framebufferHeight) { - throw new Exception("Framebuffer update rectangle too large: " + - updateRectW + "x" + updateRectH + " at (" + - updateRectX + "," + updateRectY + ")"); - } - } - - // Read CopyRect source X and Y. - - int copyRectSrcX, copyRectSrcY; - - void readCopyRect() throws IOException { - copyRectSrcX = readU16(); - copyRectSrcY = readU16(); + if (rec != null && recordFromBeginning) + for (int i = 0; i < byteCount; i++) + rec.writeByte(portion[i]); - // If the session is being recorded: - if (rec != null) { - rec.writeShortBE(copyRectSrcX); - rec.writeShortBE(copyRectSrcY); - } - } + return len; + } + // + // Write a FramebufferUpdateRequest message + // + + void writeFramebufferUpdateRequest(int x, int y, int w, int h, + boolean incremental) throws IOException { + byte[] b = new byte[10]; - // - // Read a ServerCutText message - // - - String readServerCutText() throws IOException { - skipBytes(3); - int len = readU32(); - byte[] text = new byte[len]; - readFully(text); - return new String(text); - } - + b[0] = (byte) FramebufferUpdateRequest; + b[1] = (byte) (incremental ? 1 : 0); + b[2] = (byte) ((x >> 8) & 0xff); + b[3] = (byte) (x & 0xff); + b[4] = (byte) ((y >> 8) & 0xff); + b[5] = (byte) (y & 0xff); + b[6] = (byte) ((w >> 8) & 0xff); + b[7] = (byte) (w & 0xff); + b[8] = (byte) ((h >> 8) & 0xff); + b[9] = (byte) (h & 0xff); - // - // Read an integer in compact representation (1..3 bytes). - // Such format is used as a part of the Tight encoding. - // Also, this method records data if session recording is active and - // the viewer's recordFromBeginning variable is set to true. - // + os.write(b); + } + + // + // Write a SetPixelFormat message + // + + void writeSetPixelFormat(int bitsPerPixel, int depth, boolean bigEndian, + boolean trueColour, int redMax, int greenMax, int blueMax, + int redShift, int greenShift, int blueShift) throws IOException { + byte[] b = new byte[20]; - int readCompactLen() throws IOException { - int[] portion = new int[3]; - portion[0] = readU8(); - int byteCount = 1; - int len = portion[0] & 0x7F; - if ((portion[0] & 0x80) != 0) { - portion[1] = readU8(); - byteCount++; - len |= (portion[1] & 0x7F) << 7; - if ((portion[1] & 0x80) != 0) { - portion[2] = readU8(); - byteCount++; - len |= (portion[2] & 0xFF) << 14; - } - } + b[0] = (byte) SetPixelFormat; + b[4] = (byte) bitsPerPixel; + b[5] = (byte) depth; + b[6] = (byte) (bigEndian ? 1 : 0); + b[7] = (byte) (trueColour ? 1 : 0); + b[8] = (byte) ((redMax >> 8) & 0xff); + b[9] = (byte) (redMax & 0xff); + b[10] = (byte) ((greenMax >> 8) & 0xff); + b[11] = (byte) (greenMax & 0xff); + b[12] = (byte) ((blueMax >> 8) & 0xff); + b[13] = (byte) (blueMax & 0xff); + b[14] = (byte) redShift; + b[15] = (byte) greenShift; + b[16] = (byte) blueShift; - if (rec != null && recordFromBeginning) - for (int i = 0; i < byteCount; i++) - rec.writeByte(portion[i]); + os.write(b); + } - return len; - } - + // + // Write a FixColourMapEntries message. The values in the red, green and + // blue arrays are from 0 to 65535. + // - // - // Write a FramebufferUpdateRequest message - // + void writeFixColourMapEntries(int firstColour, int nColours, int[] red, + int[] green, int[] blue) throws IOException { + byte[] b = new byte[6 + nColours * 6]; - void writeFramebufferUpdateRequest(int x, int y, int w, int h, - boolean incremental) - throws IOException - { - byte[] b = new byte[10]; + b[0] = (byte) FixColourMapEntries; + b[2] = (byte) ((firstColour >> 8) & 0xff); + b[3] = (byte) (firstColour & 0xff); + b[4] = (byte) ((nColours >> 8) & 0xff); + b[5] = (byte) (nColours & 0xff); - b[0] = (byte) FramebufferUpdateRequest; - b[1] = (byte) (incremental ? 1 : 0); - b[2] = (byte) ((x >> 8) & 0xff); - b[3] = (byte) (x & 0xff); - b[4] = (byte) ((y >> 8) & 0xff); - b[5] = (byte) (y & 0xff); - b[6] = (byte) ((w >> 8) & 0xff); - b[7] = (byte) (w & 0xff); - b[8] = (byte) ((h >> 8) & 0xff); - b[9] = (byte) (h & 0xff); + for (int i = 0; i < nColours; i++) { + b[6 + i * 6] = (byte) ((red[i] >> 8) & 0xff); + b[6 + i * 6 + 1] = (byte) (red[i] & 0xff); + b[6 + i * 6 + 2] = (byte) ((green[i] >> 8) & 0xff); + b[6 + i * 6 + 3] = (byte) (green[i] & 0xff); + b[6 + i * 6 + 4] = (byte) ((blue[i] >> 8) & 0xff); + b[6 + i * 6 + 5] = (byte) (blue[i] & 0xff); + } - os.write(b); - } - + os.write(b); + } - // - // Write a SetPixelFormat message - // + // + // Write a SetEncodings message + // - void writeSetPixelFormat(int bitsPerPixel, int depth, boolean bigEndian, - boolean trueColour, - int redMax, int greenMax, int blueMax, - int redShift, int greenShift, int blueShift) - throws IOException - { - byte[] b = new byte[20]; + void writeSetEncodings(int[] encs, int len) throws IOException { + byte[] b = new byte[4 + 4 * len]; + + b[0] = (byte) SetEncodings; + b[2] = (byte) ((len >> 8) & 0xff); + b[3] = (byte) (len & 0xff); - b[0] = (byte) SetPixelFormat; - b[4] = (byte) bitsPerPixel; - b[5] = (byte) depth; - b[6] = (byte) (bigEndian ? 1 : 0); - b[7] = (byte) (trueColour ? 1 : 0); - b[8] = (byte) ((redMax >> 8) & 0xff); - b[9] = (byte) (redMax & 0xff); - b[10] = (byte) ((greenMax >> 8) & 0xff); - b[11] = (byte) (greenMax & 0xff); - b[12] = (byte) ((blueMax >> 8) & 0xff); - b[13] = (byte) (blueMax & 0xff); - b[14] = (byte) redShift; - b[15] = (byte) greenShift; - b[16] = (byte) blueShift; + for (int i = 0; i < len; i++) { + b[4 + 4 * i] = (byte) ((encs[i] >> 24) & 0xff); + b[5 + 4 * i] = (byte) ((encs[i] >> 16) & 0xff); + b[6 + 4 * i] = (byte) ((encs[i] >> 8) & 0xff); + b[7 + 4 * i] = (byte) (encs[i] & 0xff); + } + + os.write(b); + } + + // + // Write a ClientCutText message + // - os.write(b); - } - - - // - // Write a FixColourMapEntries message. The values in the red, green and - // blue arrays are from 0 to 65535. - // + void writeClientCutText(String text) throws IOException { + byte[] b = new byte[8 + text.length()]; - void writeFixColourMapEntries(int firstColour, int nColours, - int[] red, int[] green, int[] blue) - throws IOException - { - byte[] b = new byte[6 + nColours * 6]; + b[0] = (byte) ClientCutText; + b[4] = (byte) ((text.length() >> 24) & 0xff); + b[5] = (byte) ((text.length() >> 16) & 0xff); + b[6] = (byte) ((text.length() >> 8) & 0xff); + b[7] = (byte) (text.length() & 0xff); - b[0] = (byte) FixColourMapEntries; - b[2] = (byte) ((firstColour >> 8) & 0xff); - b[3] = (byte) (firstColour & 0xff); - b[4] = (byte) ((nColours >> 8) & 0xff); - b[5] = (byte) (nColours & 0xff); + System.arraycopy(text.getBytes(), 0, b, 8, text.length()); + + os.write(b); + } - for (int i = 0; i < nColours; i++) { - b[6 + i * 6] = (byte) ((red[i] >> 8) & 0xff); - b[6 + i * 6 + 1] = (byte) (red[i] & 0xff); - b[6 + i * 6 + 2] = (byte) ((green[i] >> 8) & 0xff); - b[6 + i * 6 + 3] = (byte) (green[i] & 0xff); - b[6 + i * 6 + 4] = (byte) ((blue[i] >> 8) & 0xff); - b[6 + i * 6 + 5] = (byte) (blue[i] & 0xff); - } - - os.write(b); - } + // + // A buffer for putting pointer and keyboard events before being sent. This + // is to ensure that multiple RFB events generated from a single Java Event + // will all be sent in a single network packet. The maximum possible + // length is 4 modifier down events, a single key event followed by 4 + // modifier up events i.e. 9 key events or 72 bytes. + // + + byte[] eventBuf = new byte[72]; + int eventBufLen; + + // Useful shortcuts for modifier masks. - - // - // Write a SetEncodings message - // + final static int CTRL_MASK = InputEvent.CTRL_MASK; + final static int SHIFT_MASK = InputEvent.SHIFT_MASK; + final static int META_MASK = InputEvent.META_MASK; + final static int ALT_MASK = InputEvent.ALT_MASK; - void writeSetEncodings(int[] encs, int len) throws IOException { - byte[] b = new byte[4 + 4 * len]; + // + // Write a pointer event message. We may need to send modifier key events + // around it to set the correct modifier state. + // - b[0] = (byte) SetEncodings; - b[2] = (byte) ((len >> 8) & 0xff); - b[3] = (byte) (len & 0xff); + int pointerMask = 0; + + void writePointerEvent(MouseEvent evt) throws IOException { + int modifiers = evt.getModifiers(); - for (int i = 0; i < len; i++) { - b[4 + 4 * i] = (byte) ((encs[i] >> 24) & 0xff); - b[5 + 4 * i] = (byte) ((encs[i] >> 16) & 0xff); - b[6 + 4 * i] = (byte) ((encs[i] >> 8) & 0xff); - b[7 + 4 * i] = (byte) (encs[i] & 0xff); - } + int mask2 = 2; + int mask3 = 4; + if (viewer.options.reverseMouseButtons2And3) { + mask2 = 4; + mask3 = 2; + } - os.write(b); - } - - - // - // Write a ClientCutText message - // - - void writeClientCutText(String text) throws IOException { - byte[] b = new byte[8 + text.length()]; + // Note: For some reason, AWT does not set BUTTON1_MASK on left + // button presses. Here we think that it was the left button if + // modifiers do not include BUTTON2_MASK or BUTTON3_MASK. - b[0] = (byte) ClientCutText; - b[4] = (byte) ((text.length() >> 24) & 0xff); - b[5] = (byte) ((text.length() >> 16) & 0xff); - b[6] = (byte) ((text.length() >> 8) & 0xff); - b[7] = (byte) (text.length() & 0xff); - - System.arraycopy(text.getBytes(), 0, b, 8, text.length()); - - os.write(b); - } - + if (evt.getID() == MouseEvent.MOUSE_PRESSED) { + if ((modifiers & InputEvent.BUTTON2_MASK) != 0) { + pointerMask = mask2; + modifiers &= ~ALT_MASK; + } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) { + pointerMask = mask3; + modifiers &= ~META_MASK; + } else { + pointerMask = 1; + } + } else if (evt.getID() == MouseEvent.MOUSE_RELEASED) { + pointerMask = 0; + if ((modifiers & InputEvent.BUTTON2_MASK) != 0) { + modifiers &= ~ALT_MASK; + } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) { + modifiers &= ~META_MASK; + } + } - // - // A buffer for putting pointer and keyboard events before being sent. This - // is to ensure that multiple RFB events generated from a single Java Event - // will all be sent in a single network packet. The maximum possible - // length is 4 modifier down events, a single key event followed by 4 - // modifier up events i.e. 9 key events or 72 bytes. - // + eventBufLen = 0; + writeModifierKeyEvents(modifiers); - byte[] eventBuf = new byte[72]; - int eventBufLen; + int x = evt.getX(); + int y = evt.getY(); - - // Useful shortcuts for modifier masks. + if (x < 0) + x = 0; + if (y < 0) + y = 0; - final static int CTRL_MASK = InputEvent.CTRL_MASK; - final static int SHIFT_MASK = InputEvent.SHIFT_MASK; - final static int META_MASK = InputEvent.META_MASK; - final static int ALT_MASK = InputEvent.ALT_MASK; - - - // - // Write a pointer event message. We may need to send modifier key events - // around it to set the correct modifier state. - // + eventBuf[eventBufLen++] = (byte) PointerEvent; + eventBuf[eventBufLen++] = (byte) pointerMask; + eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff); + eventBuf[eventBufLen++] = (byte) (x & 0xff); + eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff); + eventBuf[eventBufLen++] = (byte) (y & 0xff); - int pointerMask = 0; - - void writePointerEvent(MouseEvent evt) throws IOException { - int modifiers = evt.getModifiers(); + // + // Always release all modifiers after an "up" event + // - int mask2 = 2; - int mask3 = 4; - if (viewer.options.reverseMouseButtons2And3) { - mask2 = 4; - mask3 = 2; - } + if (pointerMask == 0) { + writeModifierKeyEvents(0); + } + + os.write(eventBuf, 0, eventBufLen); + } - // Note: For some reason, AWT does not set BUTTON1_MASK on left - // button presses. Here we think that it was the left button if - // modifiers do not include BUTTON2_MASK or BUTTON3_MASK. + // + // Write a key event message. We may need to send modifier key events + // around it to set the correct modifier state. Also we need to translate + // from the Java key values to the X keysym values used by the RFB protocol. + // + + void writeKeyEvent(KeyEvent evt) throws IOException { + + int keyChar = evt.getKeyChar(); + + // + // Ignore event if only modifiers were pressed. + // - if (evt.getID() == MouseEvent.MOUSE_PRESSED) { - if ((modifiers & InputEvent.BUTTON2_MASK) != 0) { - pointerMask = mask2; - modifiers &= ~ALT_MASK; - } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) { - pointerMask = mask3; - modifiers &= ~META_MASK; - } else { - pointerMask = 1; - } - } else if (evt.getID() == MouseEvent.MOUSE_RELEASED) { - pointerMask = 0; - if ((modifiers & InputEvent.BUTTON2_MASK) != 0) { - modifiers &= ~ALT_MASK; - } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) { - modifiers &= ~META_MASK; - } - } + // Some JVMs return 0 instead of CHAR_UNDEFINED in getKeyChar(). + if (keyChar == 0) + keyChar = KeyEvent.CHAR_UNDEFINED; + + if (keyChar == KeyEvent.CHAR_UNDEFINED) { + int code = evt.getKeyCode(); + if (code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_SHIFT + || code == KeyEvent.VK_META || code == KeyEvent.VK_ALT) + return; + } + + // + // Key press or key release? + // - eventBufLen = 0; - writeModifierKeyEvents(modifiers); + boolean down = (evt.getID() == KeyEvent.KEY_PRESSED); - int x = evt.getX(); - int y = evt.getY(); - - if (x < 0) x = 0; - if (y < 0) y = 0; + int key; + if (evt.isActionKey()) { - eventBuf[eventBufLen++] = (byte) PointerEvent; - eventBuf[eventBufLen++] = (byte) pointerMask; - eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff); - eventBuf[eventBufLen++] = (byte) (x & 0xff); - eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff); - eventBuf[eventBufLen++] = (byte) (y & 0xff); - - // - // Always release all modifiers after an "up" event - // - - if (pointerMask == 0) { - writeModifierKeyEvents(0); - } - - os.write(eventBuf, 0, eventBufLen); - } - + // + // An action key should be one of the following. + // If not then just ignore the event. + // - // - // Write a key event message. We may need to send modifier key events - // around it to set the correct modifier state. Also we need to translate - // from the Java key values to the X keysym values used by the RFB protocol. - // - - void writeKeyEvent(KeyEvent evt) throws IOException { - - int keyChar = evt.getKeyChar(); - - // - // Ignore event if only modifiers were pressed. - // - - // Some JVMs return 0 instead of CHAR_UNDEFINED in getKeyChar(). - if (keyChar == 0) - keyChar = KeyEvent.CHAR_UNDEFINED; + switch (evt.getKeyCode()) { + case KeyEvent.VK_HOME: + key = 0xff50; + break; + case KeyEvent.VK_LEFT: + key = 0xff51; + break; + case KeyEvent.VK_UP: + key = 0xff52; + break; + case KeyEvent.VK_RIGHT: + key = 0xff53; + break; + case KeyEvent.VK_DOWN: + key = 0xff54; + break; + case KeyEvent.VK_PAGE_UP: + key = 0xff55; + break; + case KeyEvent.VK_PAGE_DOWN: + key = 0xff56; + break; + case KeyEvent.VK_END: + key = 0xff57; + break; + case KeyEvent.VK_INSERT: + key = 0xff63; + break; + case KeyEvent.VK_F1: + key = 0xffbe; + break; + case KeyEvent.VK_F2: + key = 0xffbf; + break; + case KeyEvent.VK_F3: + key = 0xffc0; + break; + case KeyEvent.VK_F4: + key = 0xffc1; + break; + case KeyEvent.VK_F5: + key = 0xffc2; + break; + case KeyEvent.VK_F6: + key = 0xffc3; + break; + case KeyEvent.VK_F7: + key = 0xffc4; + break; + case KeyEvent.VK_F8: + key = 0xffc5; + break; + case KeyEvent.VK_F9: + key = 0xffc6; + break; + case KeyEvent.VK_F10: + key = 0xffc7; + break; + case KeyEvent.VK_F11: + key = 0xffc8; + break; + case KeyEvent.VK_F12: + key = 0xffc9; + break; + default: + return; + } - if (keyChar == KeyEvent.CHAR_UNDEFINED) { - int code = evt.getKeyCode(); - if (code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_SHIFT || - code == KeyEvent.VK_META || code == KeyEvent.VK_ALT) - return; - } - - // - // Key press or key release? - // - - boolean down = (evt.getID() == KeyEvent.KEY_PRESSED); - - int key; - if (evt.isActionKey()) { - - // - // An action key should be one of the following. - // If not then just ignore the event. - // + } else { - switch(evt.getKeyCode()) { - case KeyEvent.VK_HOME: key = 0xff50; break; - case KeyEvent.VK_LEFT: key = 0xff51; break; - case KeyEvent.VK_UP: key = 0xff52; break; - case KeyEvent.VK_RIGHT: key = 0xff53; break; - case KeyEvent.VK_DOWN: key = 0xff54; break; - case KeyEvent.VK_PAGE_UP: key = 0xff55; break; - case KeyEvent.VK_PAGE_DOWN: key = 0xff56; break; - case KeyEvent.VK_END: key = 0xff57; break; - case KeyEvent.VK_INSERT: key = 0xff63; break; - case KeyEvent.VK_F1: key = 0xffbe; break; - case KeyEvent.VK_F2: key = 0xffbf; break; - case KeyEvent.VK_F3: key = 0xffc0; break; - case KeyEvent.VK_F4: key = 0xffc1; break; - case KeyEvent.VK_F5: key = 0xffc2; break; - case KeyEvent.VK_F6: key = 0xffc3; break; - case KeyEvent.VK_F7: key = 0xffc4; break; - case KeyEvent.VK_F8: key = 0xffc5; break; - case KeyEvent.VK_F9: key = 0xffc6; break; - case KeyEvent.VK_F10: key = 0xffc7; break; - case KeyEvent.VK_F11: key = 0xffc8; break; - case KeyEvent.VK_F12: key = 0xffc9; break; - default: - return; - } + // + // A "normal" key press. Ordinary ASCII characters go straight + // through. + // For CTRL-, CTRL is sent separately so just send . + // Backspace, tab, return, escape and delete have special keysyms. + // Anything else we ignore. + // - } else { - - // - // A "normal" key press. Ordinary ASCII characters go straight through. - // For CTRL-, CTRL is sent separately so just send . - // Backspace, tab, return, escape and delete have special keysyms. - // Anything else we ignore. - // - - key = keyChar; + key = keyChar; - if (key < 0x20) { - if (evt.isControlDown()) { - key += 0x60; - } else { - switch(key) { - case KeyEvent.VK_BACK_SPACE: key = 0xff08; break; - case KeyEvent.VK_TAB: key = 0xff09; break; - case KeyEvent.VK_ENTER: key = 0xff0d; break; - case KeyEvent.VK_ESCAPE: key = 0xff1b; break; - } - } - } else if (key == 0x7f) { - // Delete - key = 0xffff; - } else if (key > 0xff) { - // JDK1.1 on X incorrectly passes some keysyms straight through, - // so we do too. JDK1.1.4 seems to have fixed this. - // The keysyms passed are 0xff00 .. XK_BackSpace .. XK_Delete - // Also, we pass through foreign currency keysyms (0x20a0..0x20af). - if ((key < 0xff00 || key > 0xffff) && - !(key >= 0x20a0 && key <= 0x20af)) - return; - } - } + if (key < 0x20) { + if (evt.isControlDown()) { + key += 0x60; + } else { + switch (key) { + case KeyEvent.VK_BACK_SPACE: + key = 0xff08; + break; + case KeyEvent.VK_TAB: + key = 0xff09; + break; + case KeyEvent.VK_ENTER: + key = 0xff0d; + break; + case KeyEvent.VK_ESCAPE: + key = 0xff1b; + break; + } + } + } else if (key == 0x7f) { + // Delete + key = 0xffff; + } else if (key > 0xff) { + // JDK1.1 on X incorrectly passes some keysyms straight through, + // so we do too. JDK1.1.4 seems to have fixed this. + // The keysyms passed are 0xff00 .. XK_BackSpace .. XK_Delete + // Also, we pass through foreign currency keysyms + // (0x20a0..0x20af). + if ((key < 0xff00 || key > 0xffff) + && !(key >= 0x20a0 && key <= 0x20af)) + return; + } + } - // Fake keyPresses for keys that only generates keyRelease events - if ((key == 0xe5) || (key == 0xc5) || // XK_aring / XK_Aring - (key == 0xe4) || (key == 0xc4) || // XK_adiaeresis / XK_Adiaeresis - (key == 0xf6) || (key == 0xd6) || // XK_odiaeresis / XK_Odiaeresis - (key == 0xa7) || (key == 0xbd) || // XK_section / XK_onehalf - (key == 0xa3)) { // XK_sterling - // Make sure we do not send keypress events twice on platforms - // with correct JVMs (those that actually report KeyPress for all - // keys) - if (down) - brokenKeyPressed = true; + // Fake keyPresses for keys that only generates keyRelease events + if ((key == 0xe5) || (key == 0xc5) || // XK_aring / XK_Aring + (key == 0xe4) || (key == 0xc4) || // XK_adiaeresis / + // XK_Adiaeresis + (key == 0xf6) || (key == 0xd6) || // XK_odiaeresis / + // XK_Odiaeresis + (key == 0xa7) || (key == 0xbd) || // XK_section / XK_onehalf + (key == 0xa3)) { // XK_sterling + // Make sure we do not send keypress events twice on platforms + // with correct JVMs (those that actually report KeyPress for all + // keys) + if (down) + brokenKeyPressed = true; - if (!down && !brokenKeyPressed) { - // We've got a release event for this key, but haven't received - // a press. Fake it. - eventBufLen = 0; - writeModifierKeyEvents(evt.getModifiers()); - writeKeyEvent(key, true); - os.write(eventBuf, 0, eventBufLen); - } + if (!down && !brokenKeyPressed) { + // We've got a release event for this key, but haven't received + // a press. Fake it. + eventBufLen = 0; + writeModifierKeyEvents(evt.getModifiers()); + writeKeyEvent(key, true); + os.write(eventBuf, 0, eventBufLen); + } + + if (!down) + brokenKeyPressed = false; + } - if (!down) - brokenKeyPressed = false; - } - - eventBufLen = 0; - writeModifierKeyEvents(evt.getModifiers()); - writeKeyEvent(key, down); + eventBufLen = 0; + writeModifierKeyEvents(evt.getModifiers()); + writeKeyEvent(key, down); - // Always release all modifiers after an "up" event - if (!down) - writeModifierKeyEvents(0); + // Always release all modifiers after an "up" event + if (!down) + writeModifierKeyEvents(0); - os.write(eventBuf, 0, eventBufLen); - } + os.write(eventBuf, 0, eventBufLen); + } + // + // Add a raw key event with the given X keysym to eventBuf. + // - // - // Add a raw key event with the given X keysym to eventBuf. - // + void writeKeyEvent(int keysym, boolean down) { + eventBuf[eventBufLen++] = (byte) KeyboardEvent; + eventBuf[eventBufLen++] = (byte) (down ? 1 : 0); + eventBuf[eventBufLen++] = (byte) 0; + eventBuf[eventBufLen++] = (byte) 0; + eventBuf[eventBufLen++] = (byte) ((keysym >> 24) & 0xff); + eventBuf[eventBufLen++] = (byte) ((keysym >> 16) & 0xff); + eventBuf[eventBufLen++] = (byte) ((keysym >> 8) & 0xff); + eventBuf[eventBufLen++] = (byte) (keysym & 0xff); + } - void writeKeyEvent(int keysym, boolean down) { - eventBuf[eventBufLen++] = (byte) KeyboardEvent; - eventBuf[eventBufLen++] = (byte) (down ? 1 : 0); - eventBuf[eventBufLen++] = (byte) 0; - eventBuf[eventBufLen++] = (byte) 0; - eventBuf[eventBufLen++] = (byte) ((keysym >> 24) & 0xff); - eventBuf[eventBufLen++] = (byte) ((keysym >> 16) & 0xff); - eventBuf[eventBufLen++] = (byte) ((keysym >> 8) & 0xff); - eventBuf[eventBufLen++] = (byte) (keysym & 0xff); - } + // + // Write key events to set the correct modifier state. + // + int oldModifiers = 0; - // - // Write key events to set the correct modifier state. - // + void writeModifierKeyEvents(int newModifiers) { + if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK)) + writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0); - int oldModifiers = 0; + if ((newModifiers & SHIFT_MASK) != (oldModifiers & SHIFT_MASK)) + writeKeyEvent(0xffe1, (newModifiers & SHIFT_MASK) != 0); - void writeModifierKeyEvents(int newModifiers) { - if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK)) - writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0); + if ((newModifiers & META_MASK) != (oldModifiers & META_MASK)) + writeKeyEvent(0xffe7, (newModifiers & META_MASK) != 0); - if ((newModifiers & SHIFT_MASK) != (oldModifiers & SHIFT_MASK)) - writeKeyEvent(0xffe1, (newModifiers & SHIFT_MASK) != 0); - - if ((newModifiers & META_MASK) != (oldModifiers & META_MASK)) - writeKeyEvent(0xffe7, (newModifiers & META_MASK) != 0); + if ((newModifiers & ALT_MASK) != (oldModifiers & ALT_MASK)) + writeKeyEvent(0xffe9, (newModifiers & ALT_MASK) != 0); - if ((newModifiers & ALT_MASK) != (oldModifiers & ALT_MASK)) - writeKeyEvent(0xffe9, (newModifiers & ALT_MASK) != 0); + oldModifiers = newModifiers; + } - oldModifiers = newModifiers; - } + // + // Compress and write the data into the recorded session file. This + // method assumes the recording is on (rec != null). + // - // - // Compress and write the data into the recorded session file. This - // method assumes the recording is on (rec != null). - // + void recordCompressedData(byte[] data, int off, int len) throws IOException { + Deflater deflater = new Deflater(); + deflater.setInput(data, off, len); + int bufSize = len + len / 100 + 12; + byte[] buf = new byte[bufSize]; + deflater.finish(); + int compressedSize = deflater.deflate(buf); + recordCompactLen(compressedSize); + rec.write(buf, 0, compressedSize); + } - void recordCompressedData(byte[] data, int off, int len) throws IOException { - Deflater deflater = new Deflater(); - deflater.setInput(data, off, len); - int bufSize = len + len / 100 + 12; - byte[] buf = new byte[bufSize]; - deflater.finish(); - int compressedSize = deflater.deflate(buf); - recordCompactLen(compressedSize); - rec.write(buf, 0, compressedSize); - } - - void recordCompressedData(byte[] data) throws IOException { - recordCompressedData(data, 0, data.length); - } + void recordCompressedData(byte[] data) throws IOException { + recordCompressedData(data, 0, data.length); + } - // - // Write an integer in compact representation (1..3 bytes) into the - // recorded session file. This method assumes the recording is on - // (rec != null). - // + // + // Write an integer in compact representation (1..3 bytes) into the + // recorded session file. This method assumes the recording is on + // (rec != null). + // - void recordCompactLen(int len) throws IOException { - byte[] buf = new byte[3]; - int bytes = 0; - buf[bytes++] = (byte)(len & 0x7F); - if (len > 0x7F) { - buf[bytes-1] |= 0x80; - buf[bytes++] = (byte)(len >> 7 & 0x7F); - if (len > 0x3FFF) { - buf[bytes-1] |= 0x80; - buf[bytes++] = (byte)(len >> 14 & 0xFF); - } - } - rec.write(buf, 0, bytes); - } + void recordCompactLen(int len) throws IOException { + byte[] buf = new byte[3]; + int bytes = 0; + buf[bytes++] = (byte) (len & 0x7F); + if (len > 0x7F) { + buf[bytes - 1] |= 0x80; + buf[bytes++] = (byte) (len >> 7 & 0x7F); + if (len > 0x3FFF) { + buf[bytes - 1] |= 0x80; + buf[bytes++] = (byte) (len >> 14 & 0xFF); + } + } + rec.write(buf, 0, bytes); + } - public void startTiming() { - timing = true; + public void startTiming() { + timing = true; - // Carry over up to 1s worth of previous rate for smoothing. + // Carry over up to 1s worth of previous rate for smoothing. - if (timeWaitedIn100us > 10000) { - timedKbits = timedKbits * 10000 / timeWaitedIn100us; - timeWaitedIn100us = 10000; - } - } + if (timeWaitedIn100us > 10000) { + timedKbits = timedKbits * 10000 / timeWaitedIn100us; + timeWaitedIn100us = 10000; + } + } - public void stopTiming() { - timing = false; - if (timeWaitedIn100us < timedKbits/2) - timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s - } + public void stopTiming() { + timing = false; + if (timeWaitedIn100us < timedKbits / 2) + timeWaitedIn100us = timedKbits / 2; // upper limit 20Mbit/s + } - public long kbitsPerSecond() { - return timedKbits * 10000 / timeWaitedIn100us; - } + public long kbitsPerSecond() { + return timedKbits * 10000 / timeWaitedIn100us; + } - public long timeWaited() { - return timeWaitedIn100us; - } + public long timeWaited() { + return timeWaitedIn100us; + } - // - // Methods for reading data via our DataInputStream member variable (is). - // - // In addition to reading data, the readFully() methods updates variables - // used to estimate data throughput. - // + // + // Methods for reading data via our DataInputStream member variable (is). + // + // In addition to reading data, the readFully() methods updates variables + // used to estimate data throughput. + // - public void readFully(byte b[]) throws IOException { - readFully(b, 0, b.length); - } + public void readFully(byte b[]) throws IOException { + readFully(b, 0, b.length); + } - public void readFully(byte b[], int off, int len) throws IOException { - long before = 0; - if (timing) - before = System.currentTimeMillis(); + public void readFully(byte b[], int off, int len) throws IOException { + long before = 0; + if (timing) + before = System.currentTimeMillis(); - is.readFully(b, off, len); + is.readFully(b, off, len); - if (timing) { - long after = System.currentTimeMillis(); - long newTimeWaited = (after - before) * 10; - int newKbits = len * 8 / 1000; + if (timing) { + long after = System.currentTimeMillis(); + long newTimeWaited = (after - before) * 10; + int newKbits = len * 8 / 1000; - // limit rate to between 10kbit/s and 40Mbit/s + // limit rate to between 10kbit/s and 40Mbit/s - if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000; - if (newTimeWaited < newKbits/4) newTimeWaited = newKbits/4; + if (newTimeWaited > newKbits * 1000) + newTimeWaited = newKbits * 1000; + if (newTimeWaited < newKbits / 4) + newTimeWaited = newKbits / 4; - timeWaitedIn100us += newTimeWaited; - timedKbits += newKbits; - } + timeWaitedIn100us += newTimeWaited; + timedKbits += newKbits; + } - numBytesRead += len; - } + numBytesRead += len; + } - final int available() throws IOException { - return is.available(); - } + final int available() throws IOException { + return is.available(); + } - // FIXME: DataInputStream::skipBytes() is not guaranteed to skip - // exactly n bytes. Probably we don't want to use this method. - final int skipBytes(int n) throws IOException { - int r = is.skipBytes(n); - numBytesRead += r; - return r; - } + // FIXME: DataInputStream::skipBytes() is not guaranteed to skip + // exactly n bytes. Probably we don't want to use this method. + final int skipBytes(int n) throws IOException { + int r = is.skipBytes(n); + numBytesRead += r; + return r; + } - final int readU8() throws IOException { - int r = is.readUnsignedByte(); - numBytesRead++; - return r; - } + final int readU8() throws IOException { + int r = is.readUnsignedByte(); + numBytesRead++; + return r; + } - final int readU16() throws IOException { - int r = is.readUnsignedShort(); - numBytesRead += 2; - return r; - } + final int readU16() throws IOException { + int r = is.readUnsignedShort(); + numBytesRead += 2; + return r; + } - final int readU32() throws IOException { - int r = is.readInt(); - numBytesRead += 4; - return r; - } - + final int readU32() throws IOException { + int r = is.readInt(); + numBytesRead += 4; + return r; + } + } - diff -r c930d146670f -r 60a87e277536 src/VncCanvas.java --- a/src/VncCanvas.java Wed Apr 13 07:48:49 2011 +0900 +++ b/src/VncCanvas.java Wed Apr 13 08:00:53 2011 +0900 @@ -33,1854 +33,1858 @@ // VncCanvas is a subclass of Canvas which draws a VNC desktop on it. // -class VncCanvas extends Canvas - implements KeyListener, MouseListener, MouseMotionListener { - - VncViewer viewer; - RfbProto rfb; - ColorModel cm8, cm24; - Color[] colors; - int bytesPixel; +class VncCanvas extends Canvas implements KeyListener, MouseListener, + MouseMotionListener { - int maxWidth = 0, maxHeight = 0; - int scalingFactor; - int scaledWidth, scaledHeight; + VncViewer viewer; + RfbProto rfb; + ColorModel cm8, cm24; + Color[] colors; + int bytesPixel; - Image memImage; - Graphics memGraphics; + int maxWidth = 0, maxHeight = 0; + int scalingFactor; + int scaledWidth, scaledHeight; - Image rawPixelsImage; - MemoryImageSource pixelsSource; - byte[] pixels8; - int[] pixels24; + Image memImage; + Graphics memGraphics; - // 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 + Image rawPixelsImage; + MemoryImageSource pixelsSource; + byte[] pixels8; + int[] pixels24; - // ZRLE encoder's data. - byte[] zrleBuf; - int zrleBufLen = 0; - byte[] zrleTilePixels8; - int[] zrleTilePixels24; - ZlibInStream zrleInStream; - boolean zrleRecWarningShown = false; - - // Zlib encoder's data. - byte[] zlibBuf; - int zlibBufLen = 0; - Inflater zlibInflater; - - // Tight encoder's data. - final static int tightZlibBufferSize = 512; - Inflater[] tightInflaters; + // 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 - // Since JPEG images are loaded asynchronously, we have to remember - // their position in the framebuffer. Also, this jpegRect object is - // used for synchronization between the rfbThread and a JVM's thread - // which decodes and loads JPEG images. - Rectangle jpegRect; - - // True if we process keyboard and mouse events. - boolean inputEnabled; - - // - // The constructors. - // + // ZRLE encoder's data. + byte[] zrleBuf; + int zrleBufLen = 0; + byte[] zrleTilePixels8; + int[] zrleTilePixels24; + ZlibInStream zrleInStream; + boolean zrleRecWarningShown = false; - public VncCanvas(VncViewer v, int maxWidth_, int maxHeight_) - throws IOException { - - viewer = v; - maxWidth = maxWidth_; - maxHeight = maxHeight_; + // Zlib encoder's data. + byte[] zlibBuf; + int zlibBufLen = 0; + Inflater zlibInflater; - rfb = viewer.rfb; - scalingFactor = viewer.options.scalingFactor; - - tightInflaters = new Inflater[4]; - - cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6)); - cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF); + // Tight encoder's data. + final static int tightZlibBufferSize = 512; + Inflater[] tightInflaters; - colors = new Color[256]; - for (int i = 0; i < 256; i++) - colors[i] = new Color(cm8.getRGB(i)); - - setPixelFormat(); + // 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; - inputEnabled = false; - if (!viewer.options.viewOnly) - enableInput(true); - - // Keyboard listener is enabled even in view-only mode, to catch - // 'r' or 'R' key presses used to request screen update. - addKeyListener(this); - } + // True if we process keyboard and mouse events. + boolean inputEnabled; - public VncCanvas(VncViewer v) throws IOException { - this(v, 0, 0); - } - - // - // Callback methods to determine geometry of our Component. - // + // + // The constructors. + // - public Dimension getPreferredSize() { - return new Dimension(scaledWidth, scaledHeight); - } + public VncCanvas(VncViewer v, int maxWidth_, int maxHeight_) + throws IOException { - public Dimension getMinimumSize() { - return new Dimension(scaledWidth, scaledHeight); - } + viewer = v; + maxWidth = maxWidth_; + maxHeight = maxHeight_; - public Dimension getMaximumSize() { - return new Dimension(scaledWidth, scaledHeight); - } + rfb = viewer.rfb; + scalingFactor = viewer.options.scalingFactor; + + tightInflaters = new Inflater[4]; + + cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6)); + cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF); - // - // All painting is performed here. - // + colors = new Color[256]; + for (int i = 0; i < 256; i++) + colors[i] = new Color(cm8.getRGB(i)); - public void update(Graphics g) { - paint(g); - } + setPixelFormat(); + + inputEnabled = false; + if (!viewer.options.viewOnly) + enableInput(true); - 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); - } - } - } + // Keyboard listener is enabled even in view-only mode, to catch + // 'r' or 'R' key presses used to request screen update. + addKeyListener(this); + } + + public VncCanvas(VncViewer v) throws IOException { + this(v, 0, 0); + } - 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. - // + // + // Callback methods to determine geometry of our Component. + // - 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 Dimension getPreferredSize() { + return new Dimension(scaledWidth, scaledHeight); + } + + public Dimension getMinimumSize() { + return new Dimension(scaledWidth, scaledHeight); } - } - return false; // All image data was processed. - } - } + + public Dimension getMaximumSize() { + return new Dimension(scaledWidth, scaledHeight); + } - // - // 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. - // + // + // All painting is performed here. + // + + public void update(Graphics g) { + paint(g); + } - 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 paint(Graphics g) { + synchronized (memImage) { + if (rfb.framebufferWidth == scaledWidth) { + g.drawImage(memImage, 0, 0, null); + } else { + paintScaledFrameBuffer(g); + } + } + if (showSoftCursor) { + int x0 = cursorX - hotX, y0 = cursorY - hotY; + Rectangle r = new Rectangle(x0, y0, cursorWidth, cursorHeight); + if (r.intersects(g.getClipBounds())) { + g.drawImage(softCursor, x0, y0, null); + } + } + } - public void 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() { + public void paintScaledFrameBuffer(Graphics g) { + g.drawImage(memImage, 0, 0, scaledWidth, scaledHeight, null); + } - // 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 + "%"); - } + // + // Override the ImageObserver interface method to handle drawing of + // JPEG-encoded data. + // - // Update scaled framebuffer geometry. - scaledWidth = (fbWidth * scalingFactor + 50) / 100; - scaledHeight = (fbHeight * scalingFactor + 50) / 100; - - // Create new off-screen image either if it does not exist, or if - // its geometry should be changed. It's not necessary to replace - // existing image if only pixel format should be changed. - if (memImage == null) { - memImage = viewer.vncContainer.createImage(fbWidth, fbHeight); - memGraphics = memImage.getGraphics(); - } else if (memImage.getWidth(null) != fbWidth || - memImage.getHeight(null) != fbHeight) { - synchronized(memImage) { - memImage = viewer.vncContainer.createImage(fbWidth, fbHeight); - memGraphics = memImage.getGraphics(); - } - } - - // Images with raw pixels should be re-allocated on every change - // of geometry or pixel format. - if (bytesPixel == 1) { + 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. + } + } - pixels24 = null; - pixels8 = new byte[fbWidth * fbHeight]; - - pixelsSource = - new MemoryImageSource(fbWidth, fbHeight, cm8, pixels8, 0, fbWidth); - - zrleTilePixels24 = null; - zrleTilePixels8 = new byte[64 * 64]; - - } else { - - pixels8 = null; - pixels24 = new int[fbWidth * fbHeight]; - - pixelsSource = - new MemoryImageSource(fbWidth, fbHeight, cm24, pixels24, 0, fbWidth); - - zrleTilePixels8 = null; - zrleTilePixels24 = new int[64 * 64]; - - } - pixelsSource.setAnimated(true); - rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource); + // + // 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. + // - // Update the size of desktop containers. - if (viewer.inSeparateFrame) { - if (viewer.desktopScrollPane != null) - resizeDesktopFrame(); - } else { - setSize(scaledWidth, scaledHeight); - } - viewer.moveFocusToDesktop(); - } - - void resizeDesktopFrame() { - setSize(scaledWidth, scaledHeight); - - // FIXME: Find a better way to determine correct size of a - // ScrollPane. -- const - Insets insets = viewer.desktopScrollPane.getInsets(); - viewer.desktopScrollPane.setSize(scaledWidth + - 2 * Math.min(insets.left, insets.right), - scaledHeight + - 2 * Math.min(insets.top, insets.bottom)); - - viewer.vncFrame.pack(); - - // Try to limit the frame size to the screen size. - - Dimension screenSize = viewer.vncFrame.getToolkit().getScreenSize(); - Dimension frameSize = viewer.vncFrame.getSize(); - Dimension newSize = frameSize; + 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 + } + } - // 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. + 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(); + } - screenSize.height -= 30; - screenSize.width -= 30; + void updateFramebufferSize() { - 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); - } + // Useful shortcuts. + int fbWidth = rfb.framebufferWidth; + int fbHeight = rfb.framebufferHeight; - viewer.desktopScrollPane.doLayout(); - } + // 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 + "%"); + } - // - // processNormalProtocol() - executed by the rfbThread to deal with the - // RFB socket. - // - - public void processNormalProtocol() throws Exception { - - // Start/stop session recording if necessary. - viewer.checkRecordingStatus(); + // Update scaled framebuffer geometry. + scaledWidth = (fbWidth * scalingFactor + 50) / 100; + scaledHeight = (fbHeight * scalingFactor + 50) / 100; - rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, - rfb.framebufferHeight, false); - - resetStats(); - boolean statsRestarted = false; - - // - // main dispatch loop - // - - long count = 0; + // 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(); + } + } - while (true) { + // Images with raw pixels should be re-allocated on every change + // of geometry or pixel format. + if (bytesPixel == 1) { - System.out.println("\ncount="+count); - count++; - System.out.println("rfb.available()="+rfb.available()); - while(rfb.available() == 0)continue; - - // Read message type from the server. - int msgType = rfb.readServerMessageType(); - - - // Process the message depending on its type. - switch (msgType) { - case RfbProto.FramebufferUpdate: + pixels24 = null; + pixels8 = new byte[fbWidth * fbHeight]; - if (statNumUpdates == viewer.debugStatsExcludeUpdates && - !statsRestarted) { - resetStats(); - statsRestarted = true; - } else if (statNumUpdates == viewer.debugStatsMeasureUpdates && - statsRestarted) { - viewer.disconnect(); - } + pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm8, + pixels8, 0, fbWidth); - rfb.readFramebufferUpdate(); - statNumUpdates++; + zrleTilePixels24 = null; + zrleTilePixels8 = new byte[64 * 64]; - boolean cursorPosReceived = false; + } else { - for (int i = 0; i < rfb.updateNRects; i++) { + pixels8 = null; + pixels24 = new int[fbWidth * fbHeight]; - rfb.readFramebufferUpdateRectHdr(); - statNumTotalRects++; - int rx = rfb.updateRectX, ry = rfb.updateRectY; - int rw = rfb.updateRectW, rh = rfb.updateRectH; + pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm24, + pixels24, 0, fbWidth); - if (rfb.updateRectEncoding == rfb.EncodingLastRect) - break; + zrleTilePixels8 = null; + zrleTilePixels24 = new int[64 * 64]; - if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) { - rfb.setFramebufferSize(rw, rh); - updateFramebufferSize(); - break; - } + } + pixelsSource.setAnimated(true); + rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource); - if (rfb.updateRectEncoding == rfb.EncodingXCursor || - rfb.updateRectEncoding == rfb.EncodingRichCursor) { - handleCursorShapeUpdate(rfb.updateRectEncoding, rx, ry, rw, rh); - continue; - } + // Update the size of desktop containers. + if (viewer.inSeparateFrame) { + if (viewer.desktopScrollPane != null) + resizeDesktopFrame(); + } else { + setSize(scaledWidth, scaledHeight); + } + viewer.moveFocusToDesktop(); + } - if (rfb.updateRectEncoding == rfb.EncodingPointerPos) { - softCursorMove(rx, ry); - cursorPosReceived = true; - continue; - } + void resizeDesktopFrame() { + setSize(scaledWidth, scaledHeight); - long numBytesReadBefore = rfb.getNumBytesRead(); + // 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)); - rfb.startTiming(); + viewer.vncFrame.pack(); + + // Try to limit the frame size to the screen size. - 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); - } + Dimension screenSize = viewer.vncFrame.getToolkit().getScreenSize(); + Dimension frameSize = viewer.vncFrame.getSize(); + Dimension newSize = frameSize; + + // Reduce Screen Size by 30 pixels in each direction; + // This is a (poor) attempt to account for + // 1) Menu bar on Macintosh (should really also account for + // Dock on OSX). Usually 22px on top of screen. + // 2) Taxkbar on Windows (usually about 28 px on bottom) + // 3) Other obstructions. + + screenSize.height -= 30; + screenSize.width -= 30; - rfb.stopTiming(); + 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); + } - statNumPixelRects++; - statNumBytesDecoded += rw * rh * bytesPixel; - statNumBytesEncoded += - (int)(rfb.getNumBytesRead() - numBytesReadBefore); + viewer.desktopScrollPane.doLayout(); } - boolean fullUpdateNeeded = false; + // + // processNormalProtocol() - executed by the rfbThread to deal with the + // RFB socket. + // + + public void processNormalProtocol() throws Exception { + + // Start/stop session recording if necessary. + viewer.checkRecordingStatus(); + + rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, + rfb.framebufferHeight, false); + + resetStats(); + boolean statsRestarted = false; + + // + // main dispatch loop + // + + long count = 0; + + while (true) { + + System.out.println("\ncount=" + count); + count++; + System.out.println("rfb.available()=" + rfb.available()); + while (rfb.available() == 0) + continue; + + // Read message type from the server. + int msgType = rfb.readServerMessageType(); + + // Process the message depending on its type. + switch (msgType) { + case RfbProto.FramebufferUpdate: + + if (statNumUpdates == viewer.debugStatsExcludeUpdates + && !statsRestarted) { + resetStats(); + statsRestarted = true; + } else if (statNumUpdates == viewer.debugStatsMeasureUpdates + && statsRestarted) { + viewer.disconnect(); + } + + rfb.readFramebufferUpdate(); + statNumUpdates++; + + boolean cursorPosReceived = false; + + for (int i = 0; i < rfb.updateNRects; i++) { + + rfb.readFramebufferUpdateRectHdr(); + statNumTotalRects++; + int rx = rfb.updateRectX, ry = rfb.updateRectY; + int rw = rfb.updateRectW, rh = rfb.updateRectH; + + if (rfb.updateRectEncoding == rfb.EncodingLastRect) + break; - // Start/stop session recording if necessary. Request full - // update if a new session file was opened. - if (viewer.checkRecordingStatus()) - fullUpdateNeeded = true; + if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) { + rfb.setFramebufferSize(rw, rh); + updateFramebufferSize(); + break; + } + + if (rfb.updateRectEncoding == rfb.EncodingXCursor + || rfb.updateRectEncoding == rfb.EncodingRichCursor) { + handleCursorShapeUpdate(rfb.updateRectEncoding, rx, ry, + rw, rh); + continue; + } + + if (rfb.updateRectEncoding == rfb.EncodingPointerPos) { + softCursorMove(rx, ry); + cursorPosReceived = true; + continue; + } + + long numBytesReadBefore = rfb.getNumBytesRead(); + + rfb.startTiming(); + + switch (rfb.updateRectEncoding) { + case RfbProto.EncodingRaw: + statNumRectsRaw++; + handleRawRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingCopyRect: + statNumRectsCopy++; + handleCopyRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingRRE: + handleRRERect(rx, ry, rw, rh); + break; + case RfbProto.EncodingCoRRE: + handleCoRRERect(rx, ry, rw, rh); + break; + case RfbProto.EncodingHextile: + statNumRectsHextile++; + handleHextileRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingZRLE: + statNumRectsZRLE++; + handleZRLERect(rx, ry, rw, rh); + break; + case RfbProto.EncodingZlib: + handleZlibRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingTight: + statNumRectsTight++; + handleTightRect(rx, ry, rw, rh); + break; + default: + throw new Exception("Unknown RFB rectangle encoding " + + rfb.updateRectEncoding); + } + + rfb.stopTiming(); + + statNumPixelRects++; + statNumBytesDecoded += rw * rh * bytesPixel; + statNumBytesEncoded += (int) (rfb.getNumBytesRead() - numBytesReadBefore); + } + + boolean fullUpdateNeeded = false; + + // Start/stop session recording if necessary. Request full + // update if a new session file was opened. + if (viewer.checkRecordingStatus()) + fullUpdateNeeded = true; - // Defer framebuffer update request if necessary. But wake up - // immediately on keyboard or mouse event. Also, don't sleep - // if there is some data to receive, or if the last update - // included a PointerPos message. - if (viewer.deferUpdateRequests > 0 && - rfb.available() == 0 && !cursorPosReceived) { - synchronized(rfb) { - try { - rfb.wait(viewer.deferUpdateRequests); - } catch (InterruptedException e) { - } - } + // 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); + } + } + } + + // + // Handle a raw rectangle. The second form with paint==false is used + // by the Hextile decoder for raw-encoded tiles. + // + + void handleRawRect(int x, int y, int w, int h) throws IOException { + handleRawRect(x, y, w, h, true); + } + + void handleRawRect(int x, int y, int w, int h, boolean paint) + throws IOException { + + if (bytesPixel == 1) { + for (int dy = y; dy < y + h; dy++) { + rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w); + if (rfb.rec != null) { + rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w); + } + } + } else { + byte[] buf = new byte[w * 4]; + int i, offset; + for (int dy = y; dy < y + h; dy++) { + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(buf); + } + offset = dy * rfb.framebufferWidth + x; + for (i = 0; i < w; i++) { + pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16 + | (buf[i * 4 + 1] & 0xFF) << 8 + | (buf[i * 4] & 0xFF); + } + } + } + + handleUpdatedPixels(x, y, w, h); + if (paint) + scheduleRepaint(x, y, w, h); + } + + // + // Handle a CopyRect rectangle. + // + + void handleCopyRect(int x, int y, int w, int h) throws IOException { + + rfb.readCopyRect(); + memGraphics.copyArea(rfb.copyRectSrcX, rfb.copyRectSrcY, w, h, x + - rfb.copyRectSrcX, y - rfb.copyRectSrcY); + + scheduleRepaint(x, y, w, h); + } + + // + // Handle an RRE-encoded rectangle. + // + + void handleRRERect(int x, int y, int w, int h) throws IOException { + + int nSubrects = rfb.readU32(); + + byte[] bg_buf = new byte[bytesPixel]; + rfb.readFully(bg_buf); + Color pixel; + if (bytesPixel == 1) { + pixel = colors[bg_buf[0] & 0xFF]; + } else { + pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, + bg_buf[0] & 0xFF); + } + memGraphics.setColor(pixel); + memGraphics.fillRect(x, y, w, h); + + byte[] buf = new byte[nSubrects * (bytesPixel + 8)]; + rfb.readFully(buf); + DataInputStream ds = new DataInputStream(new ByteArrayInputStream(buf)); + + if (rfb.rec != null) { + rfb.rec.writeIntBE(nSubrects); + rfb.rec.write(bg_buf); + rfb.rec.write(buf); + } + + int sx, sy, sw, sh; + + for (int j = 0; j < nSubrects; j++) { + if (bytesPixel == 1) { + pixel = colors[ds.readUnsignedByte()]; + } else { + ds.skip(4); + pixel = new Color(buf[j * 12 + 2] & 0xFF, + buf[j * 12 + 1] & 0xFF, buf[j * 12] & 0xFF); + } + sx = x + ds.readUnsignedShort(); + sy = y + ds.readUnsignedShort(); + sw = ds.readUnsignedShort(); + sh = ds.readUnsignedShort(); + + memGraphics.setColor(pixel); + memGraphics.fillRect(sx, sy, sw, sh); + } + + scheduleRepaint(x, y, w, h); } - viewer.autoSelectEncodings(); + // + // Handle a CoRRE-encoded rectangle. + // + + void handleCoRRERect(int x, int y, int w, int h) throws IOException { + int nSubrects = rfb.readU32(); + + byte[] bg_buf = new byte[bytesPixel]; + rfb.readFully(bg_buf); + Color pixel; + if (bytesPixel == 1) { + pixel = colors[bg_buf[0] & 0xFF]; + } else { + pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, + bg_buf[0] & 0xFF); + } + memGraphics.setColor(pixel); + memGraphics.fillRect(x, y, w, h); + + byte[] buf = new byte[nSubrects * (bytesPixel + 4)]; + rfb.readFully(buf); + + if (rfb.rec != null) { + rfb.rec.writeIntBE(nSubrects); + rfb.rec.write(bg_buf); + rfb.rec.write(buf); + } + + int sx, sy, sw, sh; + int i = 0; + + for (int j = 0; j < nSubrects; j++) { + if (bytesPixel == 1) { + pixel = colors[buf[i++] & 0xFF]; + } else { + pixel = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF, + buf[i] & 0xFF); + i += 4; + } + sx = x + (buf[i++] & 0xFF); + sy = y + (buf[i++] & 0xFF); + sw = buf[i++] & 0xFF; + sh = buf[i++] & 0xFF; + + memGraphics.setColor(pixel); + memGraphics.fillRect(sx, sy, sw, sh); + } + + scheduleRepaint(x, y, w, h); + } + + // + // Handle a Hextile-encoded rectangle. + // + + // These colors should be kept between handleHextileSubrect() calls. + private Color hextile_bg, hextile_fg; + + void handleHextileRect(int x, int y, int w, int h) throws IOException { + + hextile_bg = new Color(0); + hextile_fg = new Color(0); + + for (int ty = y; ty < y + h; ty += 16) { + int th = 16; + if (y + h - ty < 16) + th = y + h - ty; + + for (int tx = x; tx < x + w; tx += 16) { + int tw = 16; + if (x + w - tx < 16) + tw = x + w - tx; + + handleHextileSubrect(tx, ty, tw, th); + } + + // Finished with a row of tiles, now let's show it. + scheduleRepaint(x, y, w, h); + } + } + + // + // Handle one tile in the Hextile-encoded data. + // + + void handleHextileSubrect(int tx, int ty, int tw, int th) + throws IOException { + + int subencoding = rfb.readU8(); + if (rfb.rec != null) { + rfb.rec.writeByte(subencoding); + } + + // Is it a raw-encoded sub-rectangle? + if ((subencoding & rfb.HextileRaw) != 0) { + handleRawRect(tx, ty, tw, th, false); + return; + } + + // Read and draw the background if specified. + byte[] cbuf = new byte[bytesPixel]; + if ((subencoding & rfb.HextileBackgroundSpecified) != 0) { + rfb.readFully(cbuf); + if (bytesPixel == 1) { + hextile_bg = colors[cbuf[0] & 0xFF]; + } else { + hextile_bg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, + cbuf[0] & 0xFF); + } + if (rfb.rec != null) { + rfb.rec.write(cbuf); + } + } + memGraphics.setColor(hextile_bg); + memGraphics.fillRect(tx, ty, tw, th); + + // Read the foreground color if specified. + if ((subencoding & rfb.HextileForegroundSpecified) != 0) { + rfb.readFully(cbuf); + if (bytesPixel == 1) { + hextile_fg = colors[cbuf[0] & 0xFF]; + } else { + hextile_fg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, + cbuf[0] & 0xFF); + } + if (rfb.rec != null) { + rfb.rec.write(cbuf); + } + } + + // Done with this tile if there is no sub-rectangles. + if ((subencoding & rfb.HextileAnySubrects) == 0) + return; + + int nSubrects = rfb.readU8(); + int bufsize = nSubrects * 2; + if ((subencoding & rfb.HextileSubrectsColoured) != 0) { + bufsize += nSubrects * bytesPixel; + } + byte[] buf = new byte[bufsize]; + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.writeByte(nSubrects); + rfb.rec.write(buf); + } - // 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; + int b1, b2, sx, sy, sw, sh; + int i = 0; + + if ((subencoding & rfb.HextileSubrectsColoured) == 0) { + + // Sub-rectangles are all of the same color. + memGraphics.setColor(hextile_fg); + for (int j = 0; j < nSubrects; j++) { + b1 = buf[i++] & 0xFF; + b2 = buf[i++] & 0xFF; + sx = tx + (b1 >> 4); + sy = ty + (b1 & 0xf); + sw = (b2 >> 4) + 1; + sh = (b2 & 0xf) + 1; + memGraphics.fillRect(sx, sy, sw, sh); + } + } else if (bytesPixel == 1) { + + // BGR233 (8-bit color) version for colored sub-rectangles. + for (int j = 0; j < nSubrects; j++) { + hextile_fg = colors[buf[i++] & 0xFF]; + b1 = buf[i++] & 0xFF; + b2 = buf[i++] & 0xFF; + sx = tx + (b1 >> 4); + sy = ty + (b1 & 0xf); + sw = (b2 >> 4) + 1; + sh = (b2 & 0xf) + 1; + memGraphics.setColor(hextile_fg); + memGraphics.fillRect(sx, sy, sw, sh); + } + + } else { + + // Full-color (24-bit) version for colored sub-rectangles. + for (int j = 0; j < nSubrects; j++) { + hextile_fg = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF, + buf[i] & 0xFF); + i += 4; + b1 = buf[i++] & 0xFF; + b2 = buf[i++] & 0xFF; + sx = tx + (b1 >> 4); + sy = ty + (b1 & 0xf); + sw = (b2 >> 4) + 1; + sh = (b2 & 0xf) + 1; + memGraphics.setColor(hextile_fg); + memGraphics.fillRect(sx, sy, sw, sh); + } + + } + } + + // + // Handle a ZRLE-encoded rectangle. + // + // FIXME: Currently, session recording is not fully supported for ZRLE. + // + + void handleZRLERect(int x, int y, int w, int h) throws Exception { + + if (zrleInStream == null) + zrleInStream = new ZlibInStream(); + + int nBytes = rfb.readU32(); + if (nBytes > 64 * 1024 * 1024) + throw new Exception("ZRLE decoder: illegal compressed data size"); + + if (zrleBuf == null || zrleBufLen < nBytes) { + zrleBufLen = nBytes + 4096; + zrleBuf = new byte[zrleBufLen]; + } + + // FIXME: Do not wait for all the data before decompression. + rfb.readFully(zrleBuf, 0, nBytes); + + if (rfb.rec != null) { + if (rfb.recordFromBeginning) { + rfb.rec.writeIntBE(nBytes); + rfb.rec.write(zrleBuf, 0, nBytes); + } else if (!zrleRecWarningShown) { + System.out.println("Warning: ZRLE session can be recorded" + + " only from the beginning"); + System.out.println("Warning: Recorded file may be corrupted"); + zrleRecWarningShown = true; + } + } + + zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes); + + for (int ty = y; ty < y + h; ty += 64) { + + int th = Math.min(y + h - ty, 64); + + for (int tx = x; tx < x + w; tx += 64) { + + int tw = Math.min(x + w - tx, 64); + + int mode = zrleInStream.readU8(); + boolean rle = (mode & 128) != 0; + int palSize = mode & 127; + int[] palette = new int[128]; + + readZrlePalette(palette, palSize); + + if (palSize == 1) { + int pix = palette[0]; + Color c = (bytesPixel == 1) ? colors[pix] : new Color( + 0xFF000000 | pix); + memGraphics.setColor(c); + memGraphics.fillRect(tx, ty, tw, th); + continue; + } + + if (!rle) { + if (palSize == 0) { + readZrleRawPixels(tw, th); + } else { + readZrlePackedPixels(tw, th, palette, palSize); + } + } else { + if (palSize == 0) { + readZrlePlainRLEPixels(tw, th); + } else { + readZrlePackedRLEPixels(tw, th, palette); + } + } + handleUpdatedZrleTile(tx, ty, tw, th); + } + } + + zrleInStream.reset(); + + scheduleRepaint(x, y, w, h); + } + + int readPixel(InStream is) throws Exception { + int pix; + + if (bytesPixel == 1) { + + pix = is.readU8(); + } else { + int p1 = is.readU8(); + int p2 = is.readU8(); + int p3 = is.readU8(); + pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF); + } + return pix; } - // 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; + 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)); + /* + * dst[i] = (0x00 << 16 | 0x00 << 8 | 0xFF); + */ - 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); + void readZrlePalette(int[] palette, int palSize) throws Exception { + readPixels(zrleInStream, palette, palSize); } - } - } 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); + + void readZrleRawPixels(int tw, int th) throws Exception { + if (bytesPixel == 1) { + zrleInStream.readBytes(zrleTilePixels8, 0, tw * th); + } else { + readPixels(zrleInStream, zrleTilePixels24, tw * th); // / + } } - 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); - } - } - } + + void readZrlePackedPixels(int tw, int th, int[] palette, int palSize) + throws Exception { - handleUpdatedPixels(x, y, w, h); - if (paint) - scheduleRepaint(x, y, w, h); - } - - // - // Handle a CopyRect rectangle. - // + int bppp = ((palSize > 16) ? 8 : ((palSize > 4) ? 4 + : ((palSize > 2) ? 2 : 1))); + int ptr = 0; - 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); - } + for (int i = 0; i < th; i++) { + int eol = ptr + tw; + int b = 0; + int nbits = 0; - // - // Handle an RRE-encoded rectangle. - // - - void handleRRERect(int x, int y, int w, int h) throws IOException { - - int nSubrects = rfb.readU32(); + 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]; + } + } + } + } - 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); + 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); - 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; + if (!(len <= end - ptr)) + throw new Exception("ZRLE decoder: assertion failed" + + " (len <= end-ptr)"); - 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(); + if (bytesPixel == 1) { + while (len-- > 0) + zrleTilePixels8[ptr++] = (byte) pix; + } else { + while (len-- > 0) + zrleTilePixels24[ptr++] = pix; + } + } + } - memGraphics.setColor(pixel); - memGraphics.fillRect(sx, sy, sw, sh); - } - - scheduleRepaint(x, y, w, h); - } - - // - // Handle a CoRRE-encoded rectangle. - // + void readZrlePackedRLEPixels(int tw, int th, int[] palette) + throws Exception { - void handleCoRRERect(int x, int y, int w, int h) throws IOException { - int nSubrects = 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); - 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); + if (!(len <= end - ptr)) + throw new Exception("ZRLE decoder: assertion failed" + + " (len <= end - ptr)"); + } - 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; + index &= 127; + int pix = palette[index]; - 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); - } + if (bytesPixel == 1) { + while (len-- > 0) + zrleTilePixels8[ptr++] = (byte) pix; + } else { + while (len-- > 0) + zrleTilePixels24[ptr++] = pix; + } + } + } - // - // 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; + // + // Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update. + // - 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); - } + 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); + } - // Is it a raw-encoded sub-rectangle? - if ((subencoding & rfb.HextileRaw) != 0) { - handleRawRect(tx, ty, tw, th, false); - return; - } + // + // Handle a Zlib-encoded rectangle. + // + + void handleZlibRect(int x, int y, int w, int h) throws Exception { + + int nBytes = rfb.readU32(); - // 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); + if (zlibBuf == null || zlibBufLen < nBytes) { + zlibBufLen = nBytes * 2; + zlibBuf = new byte[zlibBufLen]; + } + + rfb.readFully(zlibBuf, 0, nBytes); + + if (rfb.rec != null && rfb.recordFromBeginning) { + rfb.rec.writeIntBE(nBytes); + rfb.rec.write(zlibBuf, 0, nBytes); + } + + if (zlibInflater == null) { + zlibInflater = new Inflater(); + } + zlibInflater.setInput(zlibBuf, 0, nBytes); - // 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 (bytesPixel == 1) { + for (int dy = y; dy < y + h; dy++) { + zlibInflater.inflate(pixels8, dy * rfb.framebufferWidth + x, w); + if (rfb.rec != null && !rfb.recordFromBeginning) + rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w); + } + } else { + byte[] buf = new byte[w * 4]; + int i, offset; + for (int dy = y; dy < y + h; dy++) { + zlibInflater.inflate(buf); + offset = dy * rfb.framebufferWidth + x; + for (i = 0; i < w; i++) { + pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16 + | (buf[i * 4 + 1] & 0xFF) << 8 + | (buf[i * 4] & 0xFF); + } + if (rfb.rec != null && !rfb.recordFromBeginning) + rfb.rec.write(buf); + } + } - 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); - } + handleUpdatedPixels(x, y, w, h); + scheduleRepaint(x, y, w, h); + } - int b1, b2, sx, sy, sw, sh; - int i = 0; - - if ((subencoding & rfb.HextileSubrectsColoured) == 0) { + // + // Handle a Tight-encoded rectangle. + // - // 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) { + void handleTightRect(int x, int y, int w, int h) throws Exception { - // 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 { + 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); + } + } - // 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); - } + // Flush zlib streams if we are told by the server to do so. + for (int stream_id = 0; stream_id < 4; stream_id++) { + if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) { + tightInflaters[stream_id] = null; + } + comp_ctl >>= 1; + } - } - } + // Check correctness of subencoding value. + if (comp_ctl > rfb.TightMaxSubencoding) { + throw new Exception("Incorrect tight subencoding: " + comp_ctl); + } - // - // Handle 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(); + // Handle solid-color rectangles. + if (comp_ctl == rfb.TightFill) { - 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); + if (bytesPixel == 1) { + int idx = rfb.readU8(); + memGraphics.setColor(colors[idx]); + if (rfb.rec != null) { + rfb.rec.writeByte(idx); + } + } else { + byte[] buf = new byte[3]; + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(buf); + } + Color bg = new Color(0xFF000000 | (buf[0] & 0xFF) << 16 + | (buf[1] & 0xFF) << 8 | (buf[2] & 0xFF)); + memGraphics.setColor(bg); + } + memGraphics.fillRect(x, y, w, h); + scheduleRepaint(x, y, w, h); + return; - 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 (comp_ctl == rfb.TightJpeg) { + + statNumRectsTightJPEG++; - 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); - } - } + // 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); + } - zrleInStream.reset(); - - scheduleRepaint(x, y, w, h); - } + // Create an Image object from the JPEG data. + Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData); - int readPixel(InStream is) throws Exception { - int pix; - - if (bytesPixel == 1) { + // Remember the rectangle where the image should be drawn. + jpegRect = new Rectangle(x, y, w, h); - - 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; - } + // 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"); + } + } - 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)); - /* - dst[i] = (0x00 << 16 | - 0x00 << 8 | - 0xFF); - */ + // Done, jpegRect is not needed any more. + jpegRect = null; + return; - - - } - } - } - - void readZrlePalette(int[] palette, int palSize) throws Exception { - readPixels(zrleInStream, palette, palSize); - } + } - void readZrleRawPixels(int tw, int th) throws Exception { - if (bytesPixel == 1) { - zrleInStream.readBytes(zrleTilePixels8, 0, tw * th); - } else { - readPixels(zrleInStream, zrleTilePixels24, tw * th); /// - } - } - - void readZrlePackedPixels(int tw, int th, int[] palette, int palSize) - throws Exception { - - int bppp = ((palSize > 16) ? 8 : - ((palSize > 4) ? 4 : ((palSize > 2) ? 2 : 1))); - int ptr = 0; - - for (int i = 0; i < th; i++) { - int eol = ptr + tw; - int b = 0; - int nbits = 0; - - while (ptr < eol) { - if (nbits == 0) { - b = zrleInStream.readU8(); - nbits = 8; - } - nbits -= bppp; - int index = (b >> nbits) & ((1 << bppp) - 1) & 127; - if (bytesPixel == 1) { - zrleTilePixels8[ptr++] = (byte)palette[index]; - } else { - zrleTilePixels24[ptr++] = palette[index]; - } - } - } - } - - void readZrlePlainRLEPixels(int tw, int th) throws Exception { - int ptr = 0; - int end = ptr + tw * th; - while (ptr < end) { - int pix = readPixel(zrleInStream); - int len = 1; - int b; - do { - b = zrleInStream.readU8(); - len += b; - } while (b == 255); - - if (!(len <= end - ptr)) - throw new Exception("ZRLE decoder: assertion failed" + - " (len <= end-ptr)"); + // 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; - if (bytesPixel == 1) { - while (len-- > 0) zrleTilePixels8[ptr++] = (byte)pix; - } else { - while (len-- > 0) zrleTilePixels24[ptr++] = pix; - } - } - } - - void readZrlePackedRLEPixels(int tw, int th, int[] palette) - throws Exception { - - int ptr = 0; - int end = ptr + tw * th; - while (ptr < end) { - int index = zrleInStream.readU8(); - int len = 1; - if ((index & 128) != 0) { - int b; - do { - b = zrleInStream.readU8(); - len += b; - } while (b == 255); - - if (!(len <= end - ptr)) - throw new Exception("ZRLE decoder: assertion failed" + - " (len <= end - ptr)"); - } - - index &= 127; - int pix = palette[index]; - - if (bytesPixel == 1) { - while (len-- > 0) zrleTilePixels8[ptr++] = (byte)pix; - } else { - while (len-- > 0) zrleTilePixels24[ptr++] = pix; - } - } - } - - // - // Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update. - // - - void handleUpdatedZrleTile(int x, int y, int w, int h) { - Object src, dst; - if (bytesPixel == 1) { - src = zrleTilePixels8; dst = pixels8; - } else { - src = zrleTilePixels24; dst = pixels24; - } - int offsetSrc = 0; - int offsetDst = (y * rfb.framebufferWidth + x); - for (int j = 0; j < h; j++) { - System.arraycopy(src, offsetSrc, dst, offsetDst, w); - offsetSrc += w; - offsetDst += rfb.framebufferWidth; - } - handleUpdatedPixels(x, y, w, h); - } - - // - // Handle a Zlib-encoded rectangle. - // - - void handleZlibRect(int x, int y, int w, int h) throws Exception { - - int nBytes = rfb.readU32(); - - if (zlibBuf == null || zlibBufLen < nBytes) { - zlibBufLen = nBytes * 2; - zlibBuf = new byte[zlibBufLen]; - } - - rfb.readFully(zlibBuf, 0, nBytes); - - if (rfb.rec != null && rfb.recordFromBeginning) { - rfb.rec.writeIntBE(nBytes); - rfb.rec.write(zlibBuf, 0, nBytes); - } - - if (zlibInflater == null) { - zlibInflater = new Inflater(); - } - zlibInflater.setInput(zlibBuf, 0, nBytes); + // Read, optionally uncompress and decode data. + int dataSize = h * rowSize; + if (dataSize < rfb.TightMinToCompress) { + // Data size is small - not compressed with zlib. + if (numColors != 0) { + // Indexed colors. + byte[] indexedData = new byte[dataSize]; + rfb.readFully(indexedData); + if (rfb.rec != null) { + rfb.rec.write(indexedData); + } + if (numColors == 2) { + // Two colors. + if (bytesPixel == 1) { + decodeMonoData(x, y, w, h, indexedData, palette8); + } else { + decodeMonoData(x, y, w, h, indexedData, palette24); + } + } else { + // 3..255 colors (assuming bytesPixel == 4). + int i = 0; + for (int dy = y; dy < y + h; dy++) { + for (int dx = x; dx < x + w; dx++) { + pixels24[dy * rfb.framebufferWidth + dx] = palette24[indexedData[i++] & 0xFF]; + } + } + } + } else if (useGradient) { + // "Gradient"-processed data + byte[] buf = new byte[w * h * 3]; + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(buf); + } + decodeGradientData(x, y, w, h, buf); + } else { + // Raw truecolor data. + if (bytesPixel == 1) { + for (int dy = y; dy < y + h; dy++) { + rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w); + if (rfb.rec != null) { + rfb.rec.write(pixels8, dy * rfb.framebufferWidth + + x, w); + } + } + } else { + byte[] buf = new byte[w * 3]; + int i, offset; + for (int dy = y; dy < y + h; dy++) { + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(buf); + } + offset = dy * rfb.framebufferWidth + x; + for (i = 0; i < w; i++) { + pixels24[offset + i] = (buf[i * 3] & 0xFF) << 16 + | (buf[i * 3 + 1] & 0xFF) << 8 + | (buf[i * 3 + 2] & 0xFF); + } + } + } + } + } else { + // Data was compressed with zlib. + int zlibDataLen = rfb.readCompactLen(); + byte[] zlibData = new byte[zlibDataLen]; + rfb.readFully(zlibData); + if (rfb.rec != null && rfb.recordFromBeginning) { + rfb.rec.write(zlibData); + } + int stream_id = comp_ctl & 0x03; + if (tightInflaters[stream_id] == null) { + tightInflaters[stream_id] = new Inflater(); + } + Inflater myInflater = tightInflaters[stream_id]; + myInflater.setInput(zlibData); + byte[] buf = new byte[dataSize]; + myInflater.inflate(buf); + if (rfb.rec != null && !rfb.recordFromBeginning) { + rfb.recordCompressedData(buf); + } - if (bytesPixel == 1) { - for (int dy = y; dy < y + h; dy++) { - zlibInflater.inflate(pixels8, dy * rfb.framebufferWidth + x, w); - if (rfb.rec != null && !rfb.recordFromBeginning) - rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w); - } - } else { - byte[] buf = new byte[w * 4]; - int i, offset; - for (int dy = y; dy < y + h; dy++) { - zlibInflater.inflate(buf); - offset = dy * rfb.framebufferWidth + x; - for (i = 0; i < w; i++) { - pixels24[offset + i] = - (buf[i * 4 + 2] & 0xFF) << 16 | - (buf[i * 4 + 1] & 0xFF) << 8 | - (buf[i * 4] & 0xFF); - } - if (rfb.rec != null && !rfb.recordFromBeginning) - rfb.rec.write(buf); - } - } - - handleUpdatedPixels(x, y, w, h); - scheduleRepaint(x, y, w, h); - } - - // - // Handle a Tight-encoded rectangle. - // - - void handleTightRect(int x, int y, int w, int h) throws Exception { + if (numColors != 0) { + // Indexed colors. + if (numColors == 2) { + // Two colors. + if (bytesPixel == 1) { + decodeMonoData(x, y, w, h, buf, palette8); + } else { + decodeMonoData(x, y, w, h, buf, palette24); + } + } else { + // More than two colors (assuming bytesPixel == 4). + int i = 0; + for (int dy = y; dy < y + h; dy++) { + for (int dx = x; dx < x + w; dx++) { + pixels24[dy * rfb.framebufferWidth + dx] = palette24[buf[i++] & 0xFF]; + } + } + } + } else if (useGradient) { + // Compressed "Gradient"-filtered data (assuming bytesPixel == + // 4). + decodeGradientData(x, y, w, h, buf); + } else { + // Compressed truecolor data. + if (bytesPixel == 1) { + int destOffset = y * rfb.framebufferWidth + x; + for (int dy = 0; dy < h; dy++) { + System.arraycopy(buf, dy * w, pixels8, destOffset, w); + destOffset += rfb.framebufferWidth; + } + } else { + int srcOffset = 0; + int destOffset, i; + for (int dy = 0; dy < h; dy++) { + myInflater.inflate(buf); + destOffset = (y + dy) * rfb.framebufferWidth + x; + for (i = 0; i < w; i++) { + pixels24[destOffset + i] = (buf[srcOffset] & 0xFF) << 16 + | (buf[srcOffset + 1] & 0xFF) << 8 + | (buf[srcOffset + 2] & 0xFF); + srcOffset += 3; + } + } + } + } + } - int comp_ctl = rfb.readU8(); - if (rfb.rec != null) { - if (rfb.recordFromBeginning || - comp_ctl == (rfb.TightFill << 4) || - comp_ctl == (rfb.TightJpeg << 4)) { - // Send data exactly as received. - rfb.rec.writeByte(comp_ctl); - } else { - // Tell the decoder to flush each of the four zlib streams. - rfb.rec.writeByte(comp_ctl | 0x0F); - } - } - - // Flush zlib streams if we are told by the server to do so. - for (int stream_id = 0; stream_id < 4; stream_id++) { - if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) { - tightInflaters[stream_id] = null; - } - comp_ctl >>= 1; - } - - // Check correctness of subencoding value. - if (comp_ctl > rfb.TightMaxSubencoding) { - throw new Exception("Incorrect tight subencoding: " + comp_ctl); - } + handleUpdatedPixels(x, y, w, h); + scheduleRepaint(x, y, w, h); + } - // Handle solid-color rectangles. - if (comp_ctl == rfb.TightFill) { - - if (bytesPixel == 1) { - int idx = rfb.readU8(); - memGraphics.setColor(colors[idx]); - if (rfb.rec != null) { - rfb.rec.writeByte(idx); - } - } else { - byte[] buf = new byte[3]; - rfb.readFully(buf); - if (rfb.rec != null) { - rfb.rec.write(buf); - } - Color bg = new Color(0xFF000000 | (buf[0] & 0xFF) << 16 | - (buf[1] & 0xFF) << 8 | (buf[2] & 0xFF)); - memGraphics.setColor(bg); - } - memGraphics.fillRect(x, y, w, h); - scheduleRepaint(x, y, w, h); - return; - - } - - if (comp_ctl == rfb.TightJpeg) { - - statNumRectsTightJPEG++; + // + // Decode 1bpp-encoded bi-color rectangle (8-bit and 24-bit versions). + // - // 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 decodeMonoData(int x, int y, int w, int h, byte[] src, byte[] palette) { - // 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); + int dx, dy, n; + int i = y * rfb.framebufferWidth + x; + int rowBytes = (w + 7) / 8; + byte b; - // 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"); + 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) { - // Done, jpegRect is not needed any more. - jpegRect = null; - return; - - } + int dx, dy, n; + int i = y * rfb.framebufferWidth + x; + int rowBytes = (w + 7) / 8; + byte b; - // 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); + 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); + } } - 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; + + // + // 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++) { - // 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); + /* 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); + } } - decodeGradientData(x, y, w, h, buf); - } else { - // Raw truecolor data. - if (bytesPixel == 1) { - for (int dy = y; dy < y + h; dy++) { - rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w); - if (rfb.rec != null) { - rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w); - } - } - } else { - byte[] buf = new byte[w * 3]; - int i, offset; - for (int dy = y; dy < y + h; dy++) { - rfb.readFully(buf); - if (rfb.rec != null) { - rfb.rec.write(buf); - } - offset = dy * rfb.framebufferWidth + x; - for (i = 0; i < w; i++) { - pixels24[offset + i] = - (buf[i * 3] & 0xFF) << 16 | - (buf[i * 3 + 1] & 0xFF) << 8 | - (buf[i * 3 + 2] & 0xFF); - } - } + + // + // 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); } - } - } 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); - } + + // + // 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); + } + } - if (numColors != 0) { - // Indexed colors. - if (numColors == 2) { - // Two colors. - if (bytesPixel == 1) { - decodeMonoData(x, y, w, h, buf, palette8); - } else { - decodeMonoData(x, y, w, h, buf, palette24); - } - } else { - // More than two colors (assuming bytesPixel == 4). - int i = 0; - for (int dy = y; dy < y + h; dy++) { - for (int dx = x; dx < x + w; dx++) { - pixels24[dy * rfb.framebufferWidth + dx] = - palette24[buf[i++] & 0xFF]; - } - } + // + // Handle events. + // + + public void keyPressed(KeyEvent evt) { + processLocalKeyEvent(evt); + } + + public void keyReleased(KeyEvent evt) { + processLocalKeyEvent(evt); + } + + public void keyTyped(KeyEvent evt) { + evt.consume(); } - } 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; - } - } + + public void mousePressed(MouseEvent evt) { + processLocalMouseEvent(evt, false); } - } - } - 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; + public void mouseReleased(MouseEvent evt) { + processLocalMouseEvent(evt, false); + } - 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); - } - } + public void mouseMoved(MouseEvent evt) { + processLocalMouseEvent(evt, true); + } - 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; + public void mouseDragged(MouseEvent evt) { + processLocalMouseEvent(evt, true); + } - 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); + public void processLocalKeyEvent(KeyEvent evt) { + if (viewer.rfb != null && rfb.inNormalProtocol) { + if (!inputEnabled) { + if ((evt.getKeyChar() == 'r' || evt.getKeyChar() == 'R') + && evt.getID() == KeyEvent.KEY_PRESSED) { + // Request screen update. + try { + rfb.writeFramebufferUpdateRequest(0, 0, + rfb.framebufferWidth, rfb.framebufferHeight, + false); + } catch (IOException e) { + e.printStackTrace(); + } + } + } else { + // Input enabled. + synchronized (rfb) { + try { + rfb.writeKeyEvent(evt); + } catch (Exception e) { + e.printStackTrace(); + } + rfb.notify(); + } + } + } + // Don't ever pass keyboard events to AWT for default processing. + // Otherwise, pressing Tab would switch focus to ButtonPanel etc. + evt.consume(); + } - /* 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]; + 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(); + } + } } - 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); - } - } + // + // Ignored events. + // - // - // Display newly updated area of pixels. - // - - void handleUpdatedPixels(int x, int y, int w, int h) { + public void mouseClicked(MouseEvent evt) { + } - // 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); - } + public void mouseEntered(MouseEvent evt) { + } - // - // Tell JVM to repaint specified desktop area. - // + public void mouseExited(MouseEvent evt) { + } + + // + // Reset update statistics. + // - 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); - } - } + 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 events. - // + // //////////////////////////////////////////////////////////////// + // + // Handle cursor shape updates (XCursor and RichCursor encodings). + // - public void keyPressed(KeyEvent evt) { - processLocalKeyEvent(evt); - } - public void keyReleased(KeyEvent evt) { - processLocalKeyEvent(evt); - } - public void keyTyped(KeyEvent evt) { - evt.consume(); - } + boolean showSoftCursor = false; + + MemoryImageSource softCursorSource; + Image softCursor; + + int cursorX = 0, cursorY = 0; + int cursorWidth, cursorHeight; + int origCursorWidth, origCursorHeight; + int hotX, hotY; + int origHotX, origHotY; + + // + // Handle cursor shape update (XCursor and RichCursor encodings). + // + + synchronized void handleCursorShapeUpdate(int encodingType, int xhot, + int yhot, int width, int height) throws IOException { + + softCursorFree(); + + if (width * height == 0) + return; - 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); - } + // Ignore cursor shape data if requested by user. + if (viewer.options.ignoreCursorUpdates) { + int bytesPerRow = (width + 7) / 8; + int bytesMaskData = bytesPerRow * height; + + if (encodingType == rfb.EncodingXCursor) { + rfb.skipBytes(6 + bytesMaskData * 2); + } else { + // rfb.EncodingRichCursor + rfb.skipBytes(width * height * bytesPixel + bytesMaskData); + } + return; + } - 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(); - } + // Decode cursor pixel data. + softCursorSource = decodeCursorShape(encodingType, width, height); + + // Set original (non-scaled) cursor dimensions. + origCursorWidth = width; + origCursorHeight = height; + origHotX = xhot; + origHotY = yhot; + + // Create off-screen cursor image. + createSoftCursor(); + + // Show the cursor. + showSoftCursor = true; + repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY, + cursorWidth, cursorHeight); } - } else { - // Input enabled. - synchronized(rfb) { - try { - rfb.writeKeyEvent(evt); - } catch (Exception e) { - e.printStackTrace(); - } - rfb.notify(); - } - } - } - // Don't ever pass keyboard events to AWT for default processing. - // Otherwise, pressing Tab would switch focus to ButtonPanel etc. - evt.consume(); - } - - public void processLocalMouseEvent(MouseEvent evt, boolean moved) { - if (viewer.rfb != null && rfb.inNormalProtocol) { - if (moved) { - softCursorMove(evt.getX(), evt.getY()); - } - if (rfb.framebufferWidth != scaledWidth) { - int sx = (evt.getX() * 100 + scalingFactor/2) / scalingFactor; - int sy = (evt.getY() * 100 + scalingFactor/2) / scalingFactor; - evt.translatePoint(sx - evt.getX(), sy - evt.getY()); - } - synchronized(rfb) { - try { - rfb.writePointerEvent(evt); - } catch (Exception e) { - e.printStackTrace(); - } - rfb.notify(); - } - } - } - - // - // Ignored events. - // - - public void mouseClicked(MouseEvent evt) {} - public void mouseEntered(MouseEvent evt) {} - public void mouseExited(MouseEvent evt) {} - - // - // Reset update statistics. - // - void resetStats() { - statStartTime = System.currentTimeMillis(); - statNumUpdates = 0; - statNumTotalRects = 0; - statNumPixelRects = 0; - statNumRectsTight = 0; - statNumRectsTightJPEG = 0; - statNumRectsZRLE = 0; - statNumRectsHextile = 0; - statNumRectsRaw = 0; - statNumRectsCopy = 0; - statNumBytesEncoded = 0; - statNumBytesDecoded = 0; - } + // + // decodeCursorShape(). Decode cursor pixel data and return + // corresponding MemoryImageSource instance. + // - ////////////////////////////////////////////////////////////////// - // - // Handle cursor shape updates (XCursor and RichCursor encodings). - // + 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]; - boolean showSoftCursor = false; - - MemoryImageSource softCursorSource; - Image softCursor; - - int cursorX = 0, cursorY = 0; - int cursorWidth, cursorHeight; - int origCursorWidth, origCursorHeight; - int hotX, hotY; - int origHotX, origHotY; + if (encodingType == rfb.EncodingXCursor) { - // - // Handle cursor shape update (XCursor and RichCursor encodings). - // + // 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)) }; - synchronized void - handleCursorShapeUpdate(int encodingType, - int xhot, int yhot, int width, int height) - throws IOException { - - softCursorFree(); + // Read pixel and mask data. + byte[] pixBuf = new byte[bytesMaskData]; + rfb.readFully(pixBuf); + byte[] maskBuf = new byte[bytesMaskData]; + rfb.readFully(maskBuf); - if (width * height == 0) - return; - - // Ignore cursor shape data if requested by user. - if (viewer.options.ignoreCursorUpdates) { - int bytesPerRow = (width + 7) / 8; - int bytesMaskData = bytesPerRow * height; - - if (encodingType == rfb.EncodingXCursor) { - rfb.skipBytes(6 + bytesMaskData * 2); - } else { - // rfb.EncodingRichCursor - rfb.skipBytes(width * height * bytesPixel + bytesMaskData); - } - return; - } - - // Decode cursor pixel data. - softCursorSource = decodeCursorShape(encodingType, width, height); + // 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; + } + } - // Set original (non-scaled) cursor dimensions. - origCursorWidth = width; - origCursorHeight = height; - origHotX = xhot; - origHotY = yhot; - - // Create off-screen cursor image. - createSoftCursor(); + } else { + // encodingType == rfb.EncodingRichCursor - // Show the cursor. - showSoftCursor = true; - repaint(viewer.deferCursorUpdates, - cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight); - } - - // - // decodeCursorShape(). Decode cursor pixel data and return - // corresponding MemoryImageSource instance. - // - - synchronized MemoryImageSource - decodeCursorShape(int encodingType, int width, int height) - throws IOException { - - int bytesPerRow = (width + 7) / 8; - int bytesMaskData = bytesPerRow * height; + // Read pixel and mask data. + byte[] pixBuf = new byte[width * height * bytesPixel]; + rfb.readFully(pixBuf); + byte[] maskBuf = new byte[bytesMaskData]; + rfb.readFully(maskBuf); - 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++) { + maskByte = maskBuf[y * bytesPerRow + x]; + for (n = 7; n >= 0; n--) { + if ((maskByte >> n & 1) != 0) { + if (bytesPixel == 1) { + result = cm8.getRGB(pixBuf[i]); + } else { + result = 0xFF000000 + | (pixBuf[i * 4 + 2] & 0xFF) << 16 + | (pixBuf[i * 4 + 1] & 0xFF) << 8 + | (pixBuf[i * 4] & 0xFF); + } + } else { + result = 0; // Transparent pixel + } + softCursorPixels[i++] = result; + } + } + for (n = 7; n >= 8 - width % 8; n--) { + if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) { + if (bytesPixel == 1) { + result = cm8.getRGB(pixBuf[i]); + } else { + result = 0xFF000000 + | (pixBuf[i * 4 + 2] & 0xFF) << 16 + | (pixBuf[i * 4 + 1] & 0xFF) << 8 + | (pixBuf[i * 4] & 0xFF); + } + } else { + result = 0; // Transparent pixel + } + softCursorPixels[i++] = result; + } + } - // Decode pixel data into softCursorPixels[]. - byte pixByte, maskByte; - int x, y, n, result; - int i = 0; - for (y = 0; y < height; y++) { - for (x = 0; x < width / 8; x++) { - pixByte = pixBuf[y * bytesPerRow + x]; - maskByte = maskBuf[y * bytesPerRow + x]; - for (n = 7; n >= 0; n--) { - if ((maskByte >> n & 1) != 0) { - result = colors[pixByte >> n & 1]; - } else { - result = 0; // Transparent pixel - } - softCursorPixels[i++] = result; - } - } - for (n = 7; n >= 8 - width % 8; n--) { - if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) { - result = colors[pixBuf[y * bytesPerRow + x] >> n & 1]; - } else { - result = 0; // Transparent pixel - } - softCursorPixels[i++] = result; - } - } + } - } else { - // encodingType == rfb.EncodingRichCursor - - // Read pixel and mask data. - byte[] pixBuf = new byte[width * height * bytesPixel]; - rfb.readFully(pixBuf); - byte[] maskBuf = new byte[bytesMaskData]; - rfb.readFully(maskBuf); - - // Decode pixel data into softCursorPixels[]. - byte pixByte, maskByte; - int x, y, n, result; - int i = 0; - for (y = 0; y < height; y++) { - for (x = 0; x < width / 8; x++) { - maskByte = maskBuf[y * bytesPerRow + x]; - for (n = 7; n >= 0; n--) { - if ((maskByte >> n & 1) != 0) { - if (bytesPixel == 1) { - result = cm8.getRGB(pixBuf[i]); - } else { - result = 0xFF000000 | - (pixBuf[i * 4 + 2] & 0xFF) << 16 | - (pixBuf[i * 4 + 1] & 0xFF) << 8 | - (pixBuf[i * 4] & 0xFF); - } - } else { - result = 0; // Transparent pixel - } - softCursorPixels[i++] = result; - } + return new MemoryImageSource(width, height, softCursorPixels, 0, width); } - 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; - } - } + + // + // createSoftCursor(). Assign softCursor new Image (scaled if necessary). + // Uses softCursorSource as a source for new cursor image. + // - } + synchronized void createSoftCursor() { - return new MemoryImageSource(width, height, softCursorPixels, 0, width); - } + if (softCursorSource == null) + return; + + int scaleCursor = viewer.options.scaleCursor; + if (scaleCursor == 0 || !inputEnabled) + scaleCursor = 100; - // - // createSoftCursor(). Assign softCursor new Image (scaled if necessary). - // Uses softCursorSource as a source for new cursor image. - // - - 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; + 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); - // Save original cursor coordinates. - int x = cursorX - hotX; - int y = cursorY - hotY; - int w = cursorWidth; - int h = cursorHeight; - - cursorWidth = (origCursorWidth * scaleCursor + 50) / 100; - cursorHeight = (origCursorHeight * scaleCursor + 50) / 100; - hotX = (origHotX * scaleCursor + 50) / 100; - hotY = (origHotY * scaleCursor + 50) / 100; - softCursor = Toolkit.getDefaultToolkit().createImage(softCursorSource); + if (scaleCursor != 100) { + softCursor = softCursor.getScaledInstance(cursorWidth, + cursorHeight, Image.SCALE_SMOOTH); + } - if (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); - 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); + } + } - repaint(viewer.deferCursorUpdates, x, y, w, h); - } - } - - // - // softCursorMove(). Moves soft cursor into a particular location. - // + // + // 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); - } - } + 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. - // + // + // softCursorFree(). Remove soft cursor, dispose resources. + // - synchronized void softCursorFree() { - if (showSoftCursor) { - showSoftCursor = false; - softCursor = null; - softCursorSource = null; + synchronized void softCursorFree() { + if (showSoftCursor) { + showSoftCursor = false; + softCursor = null; + softCursorSource = null; - repaint(viewer.deferCursorUpdates, - cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight); - } - } + repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY, + cursorWidth, cursorHeight); + } + } } diff -r c930d146670f -r 60a87e277536 src/VncViewer.java --- a/src/VncViewer.java Wed Apr 13 07:48:49 2011 +0900 +++ b/src/VncViewer.java Wed Apr 13 08:00:53 2011 +0900 @@ -25,1064 +25,1049 @@ // a VNC desktop. // - import java.awt.*; - import java.awt.event.*; - import java.io.*; - import java.net.*; +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.net.*; -public class VncViewer extends java.applet.Applet - implements java.lang.Runnable, WindowListener { +public class VncViewer extends java.applet.Applet implements + java.lang.Runnable, WindowListener { - boolean inAnApplet = true; - boolean inSeparateFrame = false; + boolean inAnApplet = true; + boolean inSeparateFrame = false; - // - // main() is called when run as a java program from the command line. - // It simply runs the applet inside a newly-created frame. - // + // + // main() is called when run as a java program from the command line. + // It simply runs the applet inside a newly-created frame. + // - public static void main(String[] argv) { - VncViewer v = new VncViewer(); - v.mainArgs = argv; - v.inAnApplet = false; - v.inSeparateFrame = true; - /* - if(argv.length > 1){ - v.host = argv[0]; - v.port = Integer.parseInt(argv[1]); - } - */ - v.init(); - v.start(); - } + public static void main(String[] argv) { + VncViewer v = new VncViewer(); + v.mainArgs = argv; + v.inAnApplet = false; + v.inSeparateFrame = true; + /* + * if(argv.length > 1){ v.host = argv[0]; v.port = + * Integer.parseInt(argv[1]); } + */ + v.init(); + v.start(); + } - String[] mainArgs; + String[] mainArgs; + + RfbProto rfb; + Thread rfbThread; - RfbProto rfb; - Thread rfbThread; + Frame vncFrame; + Container vncContainer; + ScrollPane desktopScrollPane; + GridBagLayout gridbag; + ButtonPanel buttonPanel; + Label connStatusLabel; + VncCanvas vc; + OptionsFrame options; + ClipboardFrame clipboard; + RecordingFrame rec; - Frame vncFrame; - Container vncContainer; - ScrollPane desktopScrollPane; - GridBagLayout gridbag; - ButtonPanel buttonPanel; - Label connStatusLabel; - VncCanvas vc; - OptionsFrame options; - ClipboardFrame clipboard; - RecordingFrame rec; - - // Control session recording. - Object recordingSync; - String sessionFileName; - boolean recordingActive; - boolean recordingStatusChanged; - String cursorUpdatesDef; - String eightBitColorsDef; + // Control session recording. + Object recordingSync; + String sessionFileName; + boolean recordingActive; + boolean recordingStatusChanged; + String cursorUpdatesDef; + String eightBitColorsDef; - // Variables read from parameter values. - String socketFactory; - String host; - int port; - String passwordParam; - boolean showControls; - boolean offerRelogin; - boolean showOfflineDesktop; - int deferScreenUpdates; - int deferCursorUpdates; - int deferUpdateRequests; - int debugStatsExcludeUpdates; - int debugStatsMeasureUpdates; + // Variables read from parameter values. + String socketFactory; + String host; + int port; + String passwordParam; + boolean showControls; + boolean offerRelogin; + boolean showOfflineDesktop; + int deferScreenUpdates; + int deferCursorUpdates; + int deferUpdateRequests; + int debugStatsExcludeUpdates; + int debugStatsMeasureUpdates; - - // Reference to this applet for inter-applet communication. - public static java.applet.Applet refApplet; + // Reference to this applet for inter-applet communication. + public static java.applet.Applet refApplet; - // - // init() - // + // + // init() + // - public void init() { + public void init() { - readParameters(); + readParameters(); - refApplet = this; + refApplet = this; - if (inSeparateFrame) { - vncFrame = new Frame("TightVNC"); - if (!inAnApplet) { - vncFrame.add("Center", this); - } - vncContainer = vncFrame; - } else { - vncContainer = this; - } + if (inSeparateFrame) { + vncFrame = new Frame("TightVNC"); + if (!inAnApplet) { + vncFrame.add("Center", this); + } + vncContainer = vncFrame; + } else { + vncContainer = this; + } - recordingSync = new Object(); + recordingSync = new Object(); - options = new OptionsFrame(this); - clipboard = new ClipboardFrame(this); - if (RecordingFrame.checkSecurity()) - rec = new RecordingFrame(this); - - sessionFileName = null; - recordingActive = false; - recordingStatusChanged = false; - cursorUpdatesDef = null; - eightBitColorsDef = null; + options = new OptionsFrame(this); + clipboard = new ClipboardFrame(this); + if (RecordingFrame.checkSecurity()) + rec = new RecordingFrame(this); - if (inSeparateFrame) - vncFrame.addWindowListener(this); + sessionFileName = null; + recordingActive = false; + recordingStatusChanged = false; + cursorUpdatesDef = null; + eightBitColorsDef = null; - rfbThread = new Thread(this); - rfbThread.start(); - } - - public void update(Graphics g) { - } + if (inSeparateFrame) + vncFrame.addWindowListener(this); - // - // run() - executed by the rfbThread to deal with the RFB socket. - // - - public void run() { + rfbThread = new Thread(this); + rfbThread.start(); + } - gridbag = new GridBagLayout(); - vncContainer.setLayout(gridbag); + public void update(Graphics g) { + } - GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridwidth = GridBagConstraints.REMAINDER; - gbc.anchor = GridBagConstraints.NORTHWEST; + // + // run() - executed by the rfbThread to deal with the RFB socket. + // + + public void run() { - if (showControls) { - buttonPanel = new ButtonPanel(this); - gridbag.setConstraints(buttonPanel, gbc); - vncContainer.add(buttonPanel); - } + gridbag = new GridBagLayout(); + vncContainer.setLayout(gridbag); - - - /*****************************************************************************/ - vncFrame.pack(); - vncFrame.setVisible(true); - try{ - rfb = new RfbProto(host, port, this); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.anchor = GridBagConstraints.NORTHWEST; - rfb.framebufferWidth = 1680; - rfb.framebufferHeight = 1050; - rfb.bitsPerPixel = 32; - rfb.depth = 32; - rfb.bigEndian = false; - rfb.trueColour = true; - rfb.redMax = 255; - rfb.greenMax = 255; - rfb.blueMax = 255; - rfb.redShift = 16; - rfb.greenShift = 8; - rfb.blueShift = 0; - rfb.inNormalProtocol = true; + if (showControls) { + buttonPanel = new ButtonPanel(this); + gridbag.setConstraints(buttonPanel, gbc); + vncContainer.add(buttonPanel); + } + + /*****************************************************************************/ + vncFrame.pack(); + vncFrame.setVisible(true); + try { + rfb = new RfbProto(host, port, this); - createCanvas(0, 0); - }catch(IOException e) { - System.out.println("Socket error"); - System.exit(0); - }catch(Exception e ){} - - gbc.weightx = 1.0; - gbc.weighty = 1.0; - - if (inSeparateFrame) { - - // Create a panel which itself is resizeable and can hold - // non-resizeable VncCanvas component at the top left corner. - Panel canvasPanel = new Panel(); - canvasPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); - canvasPanel.add(vc); + rfb.framebufferWidth = 1680; + rfb.framebufferHeight = 1050; + rfb.bitsPerPixel = 32; + rfb.depth = 32; + rfb.bigEndian = false; + rfb.trueColour = true; + rfb.redMax = 255; + rfb.greenMax = 255; + rfb.blueMax = 255; + rfb.redShift = 16; + rfb.greenShift = 8; + rfb.blueShift = 0; + rfb.inNormalProtocol = true; - // Create a ScrollPane which will hold a panel with VncCanvas - // inside. - desktopScrollPane = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); - gbc.fill = GridBagConstraints.BOTH; - gridbag.setConstraints(desktopScrollPane, gbc); - desktopScrollPane.add(canvasPanel); + createCanvas(0, 0); + } catch (IOException e) { + System.out.println("Socket error"); + System.exit(0); + } + + gbc.weightx = 1.0; + gbc.weighty = 1.0; + + if (inSeparateFrame) { - // Finally, add our ScrollPane to the Frame window. - vncFrame.add(desktopScrollPane); - vncFrame.setTitle(rfb.desktopName); - vncFrame.pack(); - vc.resizeDesktopFrame(); + // Create a panel which itself is resizeable and can hold + // non-resizeable VncCanvas component at the top left corner. + Panel canvasPanel = new Panel(); + canvasPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); + canvasPanel.add(vc); - } else { - - // Just add the VncCanvas component to the Applet. - gridbag.setConstraints(vc, gbc); - add(vc); - validate(); + // Create a ScrollPane which will hold a panel with VncCanvas + // inside. + desktopScrollPane = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); + gbc.fill = GridBagConstraints.BOTH; + gridbag.setConstraints(desktopScrollPane, gbc); + desktopScrollPane.add(canvasPanel); - } -//*/ - /*****************************************************************************/ - -/* + // Finally, add our ScrollPane to the Frame window. + vncFrame.add(desktopScrollPane); + vncFrame.setTitle(rfb.desktopName); + vncFrame.pack(); + vc.resizeDesktopFrame(); - try { - connectAndAuthenticate(); - doProtocolInitialisation(); + } else { - // FIXME: Use auto-scaling not only in a separate frame. - if (options.autoScale && inSeparateFrame) { - Dimension screenSize; - try { - screenSize = vncContainer.getToolkit().getScreenSize(); - } catch (Exception e) { - screenSize = new Dimension(0, 0); - } - createCanvas(screenSize.width - 32, screenSize.height - 32); - } else { - createCanvas(0, 0); - } + // Just add the VncCanvas component to the Applet. + gridbag.setConstraints(vc, gbc); + add(vc); + validate(); - gbc.weightx = 1.0; - gbc.weighty = 1.0; - - if (inSeparateFrame) { + } + // */ + /*****************************************************************************/ - // Create a panel which itself is resizeable and can hold - // non-resizeable VncCanvas component at the top left corner. - Panel canvasPanel = new Panel(); - canvasPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); - canvasPanel.add(vc); - - // Create a ScrollPane which will hold a panel with VncCanvas - // inside. - desktopScrollPane = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); - gbc.fill = GridBagConstraints.BOTH; - gridbag.setConstraints(desktopScrollPane, gbc); - desktopScrollPane.add(canvasPanel); + /* + * + * try { connectAndAuthenticate(); doProtocolInitialisation(); + * + * // FIXME: Use auto-scaling not only in a separate frame. if + * (options.autoScale && inSeparateFrame) { Dimension screenSize; try { + * screenSize = vncContainer.getToolkit().getScreenSize(); } catch + * (Exception e) { screenSize = new Dimension(0, 0); } + * createCanvas(screenSize.width - 32, screenSize.height - 32); } else { + * createCanvas(0, 0); } + * + * gbc.weightx = 1.0; gbc.weighty = 1.0; + * + * if (inSeparateFrame) { + * + * // Create a panel which itself is resizeable and can hold // + * non-resizeable VncCanvas component at the top left corner. Panel + * canvasPanel = new Panel(); canvasPanel.setLayout(new + * FlowLayout(FlowLayout.LEFT, 0, 0)); canvasPanel.add(vc); + * + * // Create a ScrollPane which will hold a panel with VncCanvas // + * inside. desktopScrollPane = new + * ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); gbc.fill = + * GridBagConstraints.BOTH; gridbag.setConstraints(desktopScrollPane, + * gbc); desktopScrollPane.add(canvasPanel); + * + * // Finally, add our ScrollPane to the Frame window. + * vncFrame.add(desktopScrollPane); vncFrame.setTitle(rfb.desktopName); + * vncFrame.pack(); vc.resizeDesktopFrame(); + * + * } else { + * + * // Just add the VncCanvas component to the Applet. + * gridbag.setConstraints(vc, gbc); add(vc); validate(); + * + * } + */ - // Finally, add our ScrollPane to the Frame window. - vncFrame.add(desktopScrollPane); - vncFrame.setTitle(rfb.desktopName); - vncFrame.pack(); - vc.resizeDesktopFrame(); - - } else { + try { - // Just add the VncCanvas component to the Applet. - gridbag.setConstraints(vc, gbc); - add(vc); - validate(); + if (showControls) + buttonPanel.enableButtons(); - } -*/ - - try{ - - if (showControls) - buttonPanel.enableButtons(); - - moveFocusToDesktop(); - processNormalProtocol();//main loop + moveFocusToDesktop(); + processNormalProtocol();// main loop - } catch (NoRouteToHostException e) { - fatalError("Network error: no route to server: " + host, e); - } catch (UnknownHostException e) { - fatalError("Network error: server name unknown: " + host, e); - } catch (ConnectException e) { - fatalError("Network error: could not connect to server: " + - host + ":" + port, e); - } catch (EOFException e) { - if (showOfflineDesktop) { - e.printStackTrace(); - System.out.println("Network error: remote side closed connection"); - if (vc != null) { - vc.enableInput(false); - } - if (inSeparateFrame) { - vncFrame.setTitle(rfb.desktopName + " [disconnected]"); + } catch (NoRouteToHostException e) { + fatalError("Network error: no route to server: " + host, e); + } catch (UnknownHostException e) { + fatalError("Network error: server name unknown: " + host, e); + } catch (ConnectException e) { + fatalError("Network error: could not connect to server: " + host + + ":" + port, e); + } catch (EOFException e) { + if (showOfflineDesktop) { + e.printStackTrace(); + System.out + .println("Network error: remote side closed connection"); + if (vc != null) { + vc.enableInput(false); + } + if (inSeparateFrame) { + vncFrame.setTitle(rfb.desktopName + " [disconnected]"); + } + if (rfb != null && !rfb.closed()) + rfb.close(); + if (showControls && buttonPanel != null) { + buttonPanel.disableButtonsOnDisconnect(); + if (inSeparateFrame) { + vncFrame.pack(); + } else { + validate(); + } + } + } else { + fatalError("Network error: remote side closed connection", e); + } + } catch (IOException e) { + String str = e.getMessage(); + if (str != null && str.length() != 0) { + fatalError("Network Error: " + str, e); + } else { + fatalError(e.toString(), e); + } + } catch (Exception e) { + String str = e.getMessage(); + if (str != null && str.length() != 0) { + fatalError("Error: " + str, e); + } else { + fatalError(e.toString(), e); + } + } + } - if (rfb != null && !rfb.closed()) - rfb.close(); - if (showControls && buttonPanel != null) { - buttonPanel.disableButtonsOnDisconnect(); - if (inSeparateFrame) { - vncFrame.pack(); - } else { - validate(); - } - } - } else { - fatalError("Network error: remote side closed connection", e); - } - } catch (IOException e) { - String str = e.getMessage(); - if (str != null && str.length() != 0) { - fatalError("Network Error: " + str, e); - } else { - fatalError(e.toString(), e); - } - } catch (Exception e) { - String str = e.getMessage(); - if (str != null && str.length() != 0) { - fatalError("Error: " + str, e); - } else { - fatalError(e.toString(), e); - } - } - - } - // - // Create a VncCanvas instance. - // + // + // Create a VncCanvas instance. + // - void createCanvas(int maxWidth, int maxHeight) throws IOException { - // Determine if Java 2D API is available and use a special - // version of VncCanvas if it is present. - vc = null; - try { - // This throws ClassNotFoundException if there is no Java 2D API. - Class cl = Class.forName("java.awt.Graphics2D"); - // If we could load Graphics2D class, then we can use VncCanvas2D. - cl = Class.forName("VncCanvas2"); - Class[] argClasses = { this.getClass(), Integer.TYPE, Integer.TYPE }; - java.lang.reflect.Constructor cstr = cl.getConstructor(argClasses); - Object[] argObjects = - { this, new Integer(maxWidth), new Integer(maxHeight) }; - vc = (VncCanvas)cstr.newInstance(argObjects); - } catch (Exception e) { - System.out.println("Warning: Java 2D API is not available"); - } + void createCanvas(int maxWidth, int maxHeight) throws IOException { + // Determine if Java 2D API is available and use a special + // version of VncCanvas if it is present. + vc = null; + try { + // This throws ClassNotFoundException if there is no Java 2D API. + Class cl = Class.forName("java.awt.Graphics2D"); + // If we could load Graphics2D class, then we can use VncCanvas2D. + cl = Class.forName("VncCanvas2"); + Class[] argClasses = { this.getClass(), Integer.TYPE, Integer.TYPE }; + java.lang.reflect.Constructor cstr = cl.getConstructor(argClasses); + Object[] argObjects = { this, new Integer(maxWidth), + new Integer(maxHeight) }; + vc = (VncCanvas) cstr.newInstance(argObjects); + } catch (Exception e) { + System.out.println("Warning: Java 2D API is not available"); + } - // If we failed to create VncCanvas2D, use old VncCanvas. - if (vc == null) - vc = new VncCanvas(this, maxWidth, maxHeight); - } + // If we failed to create VncCanvas2D, use old VncCanvas. + if (vc == null) + vc = new VncCanvas(this, maxWidth, maxHeight); + } + // + // Process RFB socket messages. + // If the rfbThread is being stopped, ignore any exceptions, + // otherwise rethrow the exception so it can be handled. + // - // - // Process RFB socket messages. - // If the rfbThread is being stopped, ignore any exceptions, - // otherwise rethrow the exception so it can be handled. - // - - void processNormalProtocol() throws Exception { - try { - vc.processNormalProtocol();//main loop - } catch (Exception e) { - if (rfbThread == null) { - System.out.println("Ignoring RFB socket exceptions" + - " because applet is stopping"); - } else { - throw e; - } - } - } + void processNormalProtocol() throws Exception { + try { + vc.processNormalProtocol();// main loop + } catch (Exception e) { + if (rfbThread == null) { + System.out.println("Ignoring RFB socket exceptions" + + " because applet is stopping"); + } else { + throw e; + } + } + } + // + // Connect to the RFB server and authenticate the user. + // - // - // Connect to the RFB server and authenticate the user. - // + void connectAndAuthenticate() throws Exception { + showConnectionStatus("Initializing..."); + if (inSeparateFrame) { + vncFrame.pack(); + vncFrame.show(); + } else { + validate(); + } + + showConnectionStatus("Connecting to " + host + ", port " + port + "..."); - void connectAndAuthenticate() throws Exception - { - showConnectionStatus("Initializing..."); - if (inSeparateFrame) { - vncFrame.pack(); - vncFrame.show(); - } else { - validate(); - } + rfb = new RfbProto(host, port, this); + showConnectionStatus("Connected to server"); - showConnectionStatus("Connecting to " + host + ", port " + port + "..."); - - rfb = new RfbProto(host, port, this); - showConnectionStatus("Connected to server"); + rfb.readVersionMsg(); + showConnectionStatus("RFB server supports protocol version " + + rfb.serverMajor + "." + rfb.serverMinor); - rfb.readVersionMsg(); - showConnectionStatus("RFB server supports protocol version " + - rfb.serverMajor + "." + rfb.serverMinor); - - rfb.writeVersionMsg(); - showConnectionStatus("Using RFB protocol version " + - rfb.clientMajor + "." + rfb.clientMinor); + rfb.writeVersionMsg(); + showConnectionStatus("Using RFB protocol version " + rfb.clientMajor + + "." + rfb.clientMinor); - int secType = rfb.negotiateSecurity(); - int authType; - if (secType == RfbProto.SecTypeTight) { - showConnectionStatus("Enabling TightVNC protocol extensions"); - rfb.setupTunneling(); - authType = rfb.negotiateAuthenticationTight(); - } else { - authType = secType; - } + int secType = rfb.negotiateSecurity(); + int authType; + if (secType == RfbProto.SecTypeTight) { + showConnectionStatus("Enabling TightVNC protocol extensions"); + rfb.setupTunneling(); + authType = rfb.negotiateAuthenticationTight(); + } else { + authType = secType; + } - switch (authType) { - case RfbProto.AuthNone: - showConnectionStatus("No authentication needed"); - rfb.authenticateNone(); - break; - case RfbProto.AuthVNC: - showConnectionStatus("Performing standard VNC authentication"); - if (passwordParam != null) { - rfb.authenticateVNC(passwordParam); - } else { - String pw = askPassword(); - rfb.authenticateVNC(pw); - } - break; - default: - throw new Exception("Unknown authentication scheme " + authType); - } - } - + switch (authType) { + case RfbProto.AuthNone: + showConnectionStatus("No authentication needed"); + rfb.authenticateNone(); + break; + case RfbProto.AuthVNC: + showConnectionStatus("Performing standard VNC authentication"); + if (passwordParam != null) { + rfb.authenticateVNC(passwordParam); + } else { + String pw = askPassword(); + rfb.authenticateVNC(pw); + } + break; + default: + throw new Exception("Unknown authentication scheme " + authType); + } + } - // - // Show a message describing the connection status. - // To hide the connection status label, use (msg == null). - // + // + // Show a message describing the connection status. + // To hide the connection status label, use (msg == null). + // - void showConnectionStatus(String msg) - { - if (msg == null) { - if (vncContainer.isAncestorOf(connStatusLabel)) { - vncContainer.remove(connStatusLabel); - } - return; - } + void showConnectionStatus(String msg) { + if (msg == null) { + if (vncContainer.isAncestorOf(connStatusLabel)) { + vncContainer.remove(connStatusLabel); + } + return; + } - System.out.println(msg); + System.out.println(msg); - if (connStatusLabel == null) { - connStatusLabel = new Label("Status: " + msg); - connStatusLabel.setFont(new Font("Helvetica", Font.PLAIN, 12)); - } else { - connStatusLabel.setText("Status: " + msg); - } + if (connStatusLabel == null) { + connStatusLabel = new Label("Status: " + msg); + connStatusLabel.setFont(new Font("Helvetica", Font.PLAIN, 12)); + } else { + connStatusLabel.setText("Status: " + msg); + } - if (!vncContainer.isAncestorOf(connStatusLabel)) { - GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridwidth = GridBagConstraints.REMAINDER; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.anchor = GridBagConstraints.NORTHWEST; - gbc.weightx = 1.0; - gbc.weighty = 1.0; - gbc.insets = new Insets(20, 30, 20, 30); - gridbag.setConstraints(connStatusLabel, gbc); - vncContainer.add(connStatusLabel); - } + if (!vncContainer.isAncestorOf(connStatusLabel)) { + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.insets = new Insets(20, 30, 20, 30); + gridbag.setConstraints(connStatusLabel, gbc); + vncContainer.add(connStatusLabel); + } - if (inSeparateFrame) { - vncFrame.pack(); - } else { - validate(); - } - } - + if (inSeparateFrame) { + vncFrame.pack(); + } else { + validate(); + } + } - // - // Show an authentication panel. - // + // + // Show an authentication panel. + // - String askPassword() throws Exception - { - showConnectionStatus(null); + String askPassword() throws Exception { + showConnectionStatus(null); + + AuthPanel authPanel = new AuthPanel(this); - AuthPanel authPanel = new AuthPanel(this); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.ipadx = 100; + gbc.ipady = 50; + gridbag.setConstraints(authPanel, gbc); + vncContainer.add(authPanel); - GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridwidth = GridBagConstraints.REMAINDER; - gbc.anchor = GridBagConstraints.NORTHWEST; - gbc.weightx = 1.0; - gbc.weighty = 1.0; - gbc.ipadx = 100; - gbc.ipady = 50; - gridbag.setConstraints(authPanel, gbc); - vncContainer.add(authPanel); + if (inSeparateFrame) { + vncFrame.pack(); + } else { + validate(); + } - if (inSeparateFrame) { - vncFrame.pack(); - } else { - validate(); - } + authPanel.moveFocusToDefaultField(); + String pw = authPanel.getPassword(); + vncContainer.remove(authPanel); - authPanel.moveFocusToDefaultField(); - String pw = authPanel.getPassword(); - vncContainer.remove(authPanel); + return pw; + } - return pw; - } - + // + // Do the rest of the protocol initialisation. + // - // - // Do the rest of the protocol initialisation. - // + void doProtocolInitialisation() throws IOException { + rfb.writeClientInit(); + rfb.readServerInit(); - void doProtocolInitialisation() throws IOException - { - rfb.writeClientInit(); - rfb.readServerInit(); + System.out.println("Desktop name is " + rfb.desktopName); + System.out.println("Desktop size is " + rfb.framebufferWidth + " x " + + rfb.framebufferHeight); + + setEncodings(); - System.out.println("Desktop name is " + rfb.desktopName); - System.out.println("Desktop size is " + rfb.framebufferWidth + " x " + - rfb.framebufferHeight); + showConnectionStatus(null); + } - setEncodings(); + // + // Send current encoding list to the RFB server. + // - showConnectionStatus(null); - } - + int[] encodingsSaved; + int nEncodingsSaved; - // - // Send current encoding list to the RFB server. - // - - int[] encodingsSaved; - int nEncodingsSaved; + void setEncodings() { + setEncodings(false); + } - void setEncodings() { setEncodings(false); } - void autoSelectEncodings() { setEncodings(true); } + void autoSelectEncodings() { + setEncodings(true); + } - void setEncodings(boolean autoSelectOnly) { - if (options == null || rfb == null || !rfb.inNormalProtocol) - return; + void setEncodings(boolean autoSelectOnly) { + if (options == null || rfb == null || !rfb.inNormalProtocol) + return; - int preferredEncoding = options.preferredEncoding; - if (preferredEncoding == -1) { - long kbitsPerSecond = rfb.kbitsPerSecond(); - if (nEncodingsSaved < 1) { - // Choose Tight or ZRLE encoding for the very first update. - System.out.println("Using Tight/ZRLE encodings"); - preferredEncoding = RfbProto.EncodingTight; - } else if (kbitsPerSecond > 2000 && - encodingsSaved[0] != RfbProto.EncodingHextile) { - // Switch to Hextile if the connection speed is above 2Mbps. - System.out.println("Throughput " + kbitsPerSecond + - " kbit/s - changing to Hextile encoding"); - preferredEncoding = RfbProto.EncodingHextile; - } else if (kbitsPerSecond < 1000 && - encodingsSaved[0] != RfbProto.EncodingTight) { - // Switch to Tight/ZRLE if the connection speed is below 1Mbps. - System.out.println("Throughput " + kbitsPerSecond + - " kbit/s - changing to Tight/ZRLE encodings"); - preferredEncoding = RfbProto.EncodingTight; - } else { - // Don't change the encoder. - if (autoSelectOnly) - return; - preferredEncoding = encodingsSaved[0]; - } - } else { - // Auto encoder selection is not enabled. - if (autoSelectOnly) - return; - } + int preferredEncoding = options.preferredEncoding; + if (preferredEncoding == -1) { + long kbitsPerSecond = rfb.kbitsPerSecond(); + if (nEncodingsSaved < 1) { + // Choose Tight or ZRLE encoding for the very first update. + System.out.println("Using Tight/ZRLE encodings"); + preferredEncoding = RfbProto.EncodingTight; + } else if (kbitsPerSecond > 2000 + && encodingsSaved[0] != RfbProto.EncodingHextile) { + // Switch to Hextile if the connection speed is above 2Mbps. + System.out.println("Throughput " + kbitsPerSecond + + " kbit/s - changing to Hextile encoding"); + preferredEncoding = RfbProto.EncodingHextile; + } else if (kbitsPerSecond < 1000 + && encodingsSaved[0] != RfbProto.EncodingTight) { + // Switch to Tight/ZRLE if the connection speed is below 1Mbps. + System.out.println("Throughput " + kbitsPerSecond + + " kbit/s - changing to Tight/ZRLE encodings"); + preferredEncoding = RfbProto.EncodingTight; + } else { + // Don't change the encoder. + if (autoSelectOnly) + return; + preferredEncoding = encodingsSaved[0]; + } + } else { + // Auto encoder selection is not enabled. + if (autoSelectOnly) + return; + } - int[] encodings = new int[20]; - int nEncodings = 0; + int[] encodings = new int[20]; + int nEncodings = 0; - encodings[nEncodings++] = preferredEncoding; - if (options.useCopyRect) { - encodings[nEncodings++] = RfbProto.EncodingCopyRect; - } + encodings[nEncodings++] = preferredEncoding; + if (options.useCopyRect) { + encodings[nEncodings++] = RfbProto.EncodingCopyRect; + } - if (preferredEncoding != RfbProto.EncodingTight) { - encodings[nEncodings++] = RfbProto.EncodingTight; - } - if (preferredEncoding != RfbProto.EncodingZRLE) { - encodings[nEncodings++] = RfbProto.EncodingZRLE; - } - if (preferredEncoding != RfbProto.EncodingHextile) { - encodings[nEncodings++] = RfbProto.EncodingHextile; - } - if (preferredEncoding != RfbProto.EncodingZlib) { - encodings[nEncodings++] = RfbProto.EncodingZlib; - } - if (preferredEncoding != RfbProto.EncodingCoRRE) { - encodings[nEncodings++] = RfbProto.EncodingCoRRE; - } - if (preferredEncoding != RfbProto.EncodingRRE) { - encodings[nEncodings++] = RfbProto.EncodingRRE; - } + if (preferredEncoding != RfbProto.EncodingTight) { + encodings[nEncodings++] = RfbProto.EncodingTight; + } + if (preferredEncoding != RfbProto.EncodingZRLE) { + encodings[nEncodings++] = RfbProto.EncodingZRLE; + } + if (preferredEncoding != RfbProto.EncodingHextile) { + encodings[nEncodings++] = RfbProto.EncodingHextile; + } + if (preferredEncoding != RfbProto.EncodingZlib) { + encodings[nEncodings++] = RfbProto.EncodingZlib; + } + if (preferredEncoding != RfbProto.EncodingCoRRE) { + encodings[nEncodings++] = RfbProto.EncodingCoRRE; + } + if (preferredEncoding != RfbProto.EncodingRRE) { + encodings[nEncodings++] = RfbProto.EncodingRRE; + } + + if (options.compressLevel >= 0 && options.compressLevel <= 9) { + encodings[nEncodings++] = RfbProto.EncodingCompressLevel0 + + options.compressLevel; + } + if (options.jpegQuality >= 0 && options.jpegQuality <= 9) { + encodings[nEncodings++] = RfbProto.EncodingQualityLevel0 + + options.jpegQuality; + } - if (options.compressLevel >= 0 && options.compressLevel <= 9) { - encodings[nEncodings++] = - RfbProto.EncodingCompressLevel0 + options.compressLevel; - } - if (options.jpegQuality >= 0 && options.jpegQuality <= 9) { - encodings[nEncodings++] = - RfbProto.EncodingQualityLevel0 + options.jpegQuality; - } + if (options.requestCursorUpdates) { + encodings[nEncodings++] = RfbProto.EncodingXCursor; + encodings[nEncodings++] = RfbProto.EncodingRichCursor; + if (!options.ignoreCursorUpdates) + encodings[nEncodings++] = RfbProto.EncodingPointerPos; + } + + encodings[nEncodings++] = RfbProto.EncodingLastRect; + encodings[nEncodings++] = RfbProto.EncodingNewFBSize; - if (options.requestCursorUpdates) { - encodings[nEncodings++] = RfbProto.EncodingXCursor; - encodings[nEncodings++] = RfbProto.EncodingRichCursor; - if (!options.ignoreCursorUpdates) - encodings[nEncodings++] = RfbProto.EncodingPointerPos; - } - - encodings[nEncodings++] = RfbProto.EncodingLastRect; - encodings[nEncodings++] = RfbProto.EncodingNewFBSize; - - boolean encodingsWereChanged = false; - if (nEncodings != nEncodingsSaved) { - encodingsWereChanged = true; - } else { - for (int i = 0; i < nEncodings; i++) { - if (encodings[i] != encodingsSaved[i]) { - encodingsWereChanged = true; - break; - } - } - } + boolean encodingsWereChanged = false; + if (nEncodings != nEncodingsSaved) { + encodingsWereChanged = true; + } else { + for (int i = 0; i < nEncodings; i++) { + if (encodings[i] != encodingsSaved[i]) { + encodingsWereChanged = true; + break; + } + } + } - if (encodingsWereChanged) { - try { - rfb.writeSetEncodings(encodings, nEncodings); - if (vc != null) { - vc.softCursorFree(); - } - } catch (Exception e) { - e.printStackTrace(); - } - encodingsSaved = encodings; - nEncodingsSaved = nEncodings; - } - } + if (encodingsWereChanged) { + try { + rfb.writeSetEncodings(encodings, nEncodings); + if (vc != null) { + vc.softCursorFree(); + } + } catch (Exception e) { + e.printStackTrace(); + } + encodingsSaved = encodings; + nEncodingsSaved = nEncodings; + } + } - - // - // setCutText() - send the given cut text to the RFB server. - // + // + // setCutText() - send the given cut text to the RFB server. + // - void setCutText(String text) { - try { - if (rfb != null && rfb.inNormalProtocol) { - rfb.writeClientCutText(text); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - + void setCutText(String text) { + try { + if (rfb != null && rfb.inNormalProtocol) { + rfb.writeClientCutText(text); + } + } catch (Exception e) { + e.printStackTrace(); + } + } - // - // Order change in session recording status. To stop recording, pass - // null in place of the fname argument. - // + // + // Order change in session recording status. To stop recording, pass + // null in place of the fname argument. + // - void setRecordingStatus(String fname) { - synchronized(recordingSync) { - sessionFileName = fname; - recordingStatusChanged = true; - } - } + void setRecordingStatus(String fname) { + synchronized (recordingSync) { + sessionFileName = fname; + recordingStatusChanged = true; + } + } + + // + // Start or stop session recording. Returns true if this method call + // causes recording of a new session. + // - // - // Start or stop session recording. Returns true if this method call - // causes recording of a new session. - // + boolean checkRecordingStatus() throws IOException { + synchronized (recordingSync) { + if (recordingStatusChanged) { + recordingStatusChanged = false; + if (sessionFileName != null) { + startRecording(); + return true; + } else { + stopRecording(); + } + } + } + return false; + } - boolean checkRecordingStatus() throws IOException { - synchronized(recordingSync) { - if (recordingStatusChanged) { - recordingStatusChanged = false; - if (sessionFileName != null) { - startRecording(); - return true; - } else { - stopRecording(); - } - } - } - return false; - } - - // - // Start session recording. - // + // + // Start session recording. + // - protected void startRecording() throws IOException { - synchronized(recordingSync) { - if (!recordingActive) { - // Save settings to restore them after recording the session. - cursorUpdatesDef = - options.choices[options.cursorUpdatesIndex].getSelectedItem(); - eightBitColorsDef = - options.choices[options.eightBitColorsIndex].getSelectedItem(); - // Set options to values suitable for recording. - options.choices[options.cursorUpdatesIndex].select("Disable"); - options.choices[options.cursorUpdatesIndex].setEnabled(false); - options.setEncodings(); - options.choices[options.eightBitColorsIndex].select("No"); - options.choices[options.eightBitColorsIndex].setEnabled(false); - options.setColorFormat(); - } else { - rfb.closeSession(); - } + protected void startRecording() throws IOException { + synchronized (recordingSync) { + if (!recordingActive) { + // Save settings to restore them after recording the session. + cursorUpdatesDef = options.choices[options.cursorUpdatesIndex] + .getSelectedItem(); + eightBitColorsDef = options.choices[options.eightBitColorsIndex] + .getSelectedItem(); + // Set options to values suitable for recording. + options.choices[options.cursorUpdatesIndex].select("Disable"); + options.choices[options.cursorUpdatesIndex].setEnabled(false); + options.setEncodings(); + options.choices[options.eightBitColorsIndex].select("No"); + options.choices[options.eightBitColorsIndex].setEnabled(false); + options.setColorFormat(); + } else { + rfb.closeSession(); + } + + System.out.println("Recording the session in " + sessionFileName); + rfb.startSession(sessionFileName); + recordingActive = true; + } + } - System.out.println("Recording the session in " + sessionFileName); - rfb.startSession(sessionFileName); - recordingActive = true; - } - } - - // - // Stop session recording. - // + // + // Stop session recording. + // - protected void stopRecording() throws IOException { - synchronized(recordingSync) { - if (recordingActive) { - // Restore options. - options.choices[options.cursorUpdatesIndex].select(cursorUpdatesDef); - options.choices[options.cursorUpdatesIndex].setEnabled(true); - options.setEncodings(); - options.choices[options.eightBitColorsIndex].select(eightBitColorsDef); - options.choices[options.eightBitColorsIndex].setEnabled(true); - options.setColorFormat(); + protected void stopRecording() throws IOException { + synchronized (recordingSync) { + if (recordingActive) { + // Restore options. + options.choices[options.cursorUpdatesIndex] + .select(cursorUpdatesDef); + options.choices[options.cursorUpdatesIndex].setEnabled(true); + options.setEncodings(); + options.choices[options.eightBitColorsIndex] + .select(eightBitColorsDef); + options.choices[options.eightBitColorsIndex].setEnabled(true); + options.setColorFormat(); + + rfb.closeSession(); + System.out.println("Session recording stopped."); + } + sessionFileName = null; + recordingActive = false; + } + } - rfb.closeSession(); - System.out.println("Session recording stopped."); - } - sessionFileName = null; - recordingActive = false; - } - } - - - // - // readParameters() - read parameters from the html source or from the - // command line. On the command line, the arguments are just a sequence of - // param_name/param_value pairs where the names and values correspond to - // those expected in the html applet tag source. - // + // + // readParameters() - read parameters from the html source or from the + // command line. On the command line, the arguments are just a sequence of + // param_name/param_value pairs where the names and values correspond to + // those expected in the html applet tag source. + // - void readParameters() { - // host = readParameter("HOST", !inAnApplet); - if(mainArgs.length > 0) host = mainArgs[0]; - else host = "hades.cr.ie.u-ryukyu.ac.jp"; - /* - if (host == null) { - host = getCodeBase().getHost(); - if (host.equals("")) { - fatalError("HOST parameter not specified"); - } - } - */ + void readParameters() { + // host = readParameter("HOST", !inAnApplet); + if (mainArgs.length > 0) + host = mainArgs[0]; + else + host = "hades.cr.ie.u-ryukyu.ac.jp"; + /* + * if (host == null) { host = getCodeBase().getHost(); if + * (host.equals("")) { fatalError("HOST parameter not specified"); } } + */ - // port = readIntParameter("PORT", 5900); - if(mainArgs.length > 1) port = Integer.parseInt(mainArgs[1]); - else port = 5550; - // Read "ENCPASSWORD" or "PASSWORD" parameter if specified. - readPasswordParameters(); + // port = readIntParameter("PORT", 5900); + if (mainArgs.length > 1) + port = Integer.parseInt(mainArgs[1]); + else + port = 5550; + // Read "ENCPASSWORD" or "PASSWORD" parameter if specified. + readPasswordParameters(); + + String str; + if (inAnApplet) { + str = readParameter("Open New Window", false); + if (str != null && str.equalsIgnoreCase("Yes")) + inSeparateFrame = true; + } - String str; - if (inAnApplet) { - str = readParameter("Open New Window", false); - if (str != null && str.equalsIgnoreCase("Yes")) - inSeparateFrame = true; - } + // "Show Controls" set to "No" disables button panel. + showControls = true; + str = readParameter("Show Controls", false); + if (str != null && str.equalsIgnoreCase("No")) + showControls = false; - // "Show Controls" set to "No" disables button panel. - showControls = true; - str = readParameter("Show Controls", false); - if (str != null && str.equalsIgnoreCase("No")) - showControls = false; + // "Offer Relogin" set to "No" disables "Login again" and "Close + // window" buttons under error messages in applet mode. + offerRelogin = true; + str = readParameter("Offer Relogin", false); + if (str != null && str.equalsIgnoreCase("No")) + offerRelogin = false; + + // Do we continue showing desktop on remote disconnect? + showOfflineDesktop = false; + str = readParameter("Show Offline Desktop", false); + if (str != null && str.equalsIgnoreCase("Yes")) + showOfflineDesktop = true; - // "Offer Relogin" set to "No" disables "Login again" and "Close - // window" buttons under error messages in applet mode. - offerRelogin = true; - str = readParameter("Offer Relogin", false); - if (str != null && str.equalsIgnoreCase("No")) - offerRelogin = false; + // Fine tuning options. + deferScreenUpdates = readIntParameter("Defer screen updates", 20); + deferCursorUpdates = readIntParameter("Defer cursor updates", 10); + deferUpdateRequests = readIntParameter("Defer update requests", 0); - // Do we continue showing desktop on remote disconnect? - showOfflineDesktop = false; - str = readParameter("Show Offline Desktop", false); - if (str != null && str.equalsIgnoreCase("Yes")) - showOfflineDesktop = true; + // Debugging options. + debugStatsExcludeUpdates = readIntParameter("DEBUG_XU", 0); + debugStatsMeasureUpdates = readIntParameter("DEBUG_CU", 0); - // Fine tuning options. - deferScreenUpdates = readIntParameter("Defer screen updates", 20); - deferCursorUpdates = readIntParameter("Defer cursor updates", 10); - deferUpdateRequests = readIntParameter("Defer update requests", 0); + // SocketFactory. + socketFactory = readParameter("SocketFactory", false); + } - // Debugging options. - debugStatsExcludeUpdates = readIntParameter("DEBUG_XU", 0); - debugStatsMeasureUpdates = readIntParameter("DEBUG_CU", 0); - - // SocketFactory. - socketFactory = readParameter("SocketFactory", false); - } + // + // Read password parameters. If an "ENCPASSWORD" parameter is set, + // then decrypt the password into the passwordParam string. Otherwise, + // try to read the "PASSWORD" parameter directly to passwordParam. + // - // - // Read password parameters. If an "ENCPASSWORD" parameter is set, - // then decrypt the password into the passwordParam string. Otherwise, - // try to read the "PASSWORD" parameter directly to passwordParam. - // - - private void readPasswordParameters() { - String encPasswordParam = readParameter("ENCPASSWORD", false); - if (encPasswordParam == null) { - passwordParam = readParameter("PASSWORD", false); + private void readPasswordParameters() { + String encPasswordParam = readParameter("ENCPASSWORD", false); + if (encPasswordParam == null) { + passwordParam = readParameter("PASSWORD", false); - } else { - // ENCPASSWORD is hexascii-encoded. Decode. - byte[] pw = {0, 0, 0, 0, 0, 0, 0, 0}; - int len = encPasswordParam.length() / 2; - if (len > 8) - len = 8; - for (int i = 0; i < len; i++) { - String hex = encPasswordParam.substring(i*2, i*2+2); - Integer x = new Integer(Integer.parseInt(hex, 16)); - pw[i] = x.byteValue(); - } - // Decrypt the password. - byte[] key = {23, 82, 107, 6, 35, 78, 88, 7}; - DesCipher des = new DesCipher(key); - des.decrypt(pw, 0, pw, 0); - passwordParam = new String(pw); + } else { + // ENCPASSWORD is hexascii-encoded. Decode. + byte[] pw = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int len = encPasswordParam.length() / 2; + if (len > 8) + len = 8; + for (int i = 0; i < len; i++) { + String hex = encPasswordParam.substring(i * 2, i * 2 + 2); + Integer x = new Integer(Integer.parseInt(hex, 16)); + pw[i] = x.byteValue(); + } + // Decrypt the password. + byte[] key = { 23, 82, 107, 6, 35, 78, 88, 7 }; + DesCipher des = new DesCipher(key); + des.decrypt(pw, 0, pw, 0); + passwordParam = new String(pw); + + } + } - } - } - - public String readParameter(String name, boolean required) { - if (inAnApplet) { - String s = getParameter(name); - if ((s == null) && required) { - fatalError(name + " parameter not specified"); - } - return s; - } + public String readParameter(String name, boolean required) { + if (inAnApplet) { + String s = getParameter(name); + if ((s == null) && required) { + fatalError(name + " parameter not specified"); + } + return s; + } - for (int i = 0; i < mainArgs.length; i += 2) { - if (mainArgs[i].equalsIgnoreCase(name)) { - try { - return mainArgs[i+1]; - } catch (Exception e) { - if (required) { - fatalError(name + " parameter not specified"); - } - return null; + for (int i = 0; i < mainArgs.length; i += 2) { + if (mainArgs[i].equalsIgnoreCase(name)) { + try { + return mainArgs[i + 1]; + } catch (Exception e) { + if (required) { + fatalError(name + " parameter not specified"); + } + return null; + } + } + } + if (required) { + fatalError(name + " parameter not specified"); + } + return null; } - } - } - if (required) { - fatalError(name + " parameter not specified"); - } - return null; - } + + int readIntParameter(String name, int defaultValue) { + String str = readParameter(name, false); + int result = defaultValue; + if (str != null) { + try { + result = Integer.parseInt(str); + } catch (NumberFormatException e) { + } + } + return result; + } - int readIntParameter(String name, int defaultValue) { - String str = readParameter(name, false); - int result = defaultValue; - if (str != null) { - try { - result = Integer.parseInt(str); - } catch (NumberFormatException e) { } - } - return result; - } + // + // moveFocusToDesktop() - move keyboard focus either to VncCanvas. + // - // - // moveFocusToDesktop() - move keyboard focus either to VncCanvas. - // + void moveFocusToDesktop() { + if (vncContainer != null) { + if (vc != null && vncContainer.isAncestorOf(vc)) + vc.requestFocus(); + } + } - void moveFocusToDesktop() { - if (vncContainer != null) { - if (vc != null && vncContainer.isAncestorOf(vc)) - vc.requestFocus(); - } - } + // + // disconnect() - close connection to server. + // - // - // disconnect() - close connection to server. - // - - synchronized public void disconnect() { - System.out.println("Disconnecting"); + synchronized public void disconnect() { + System.out.println("Disconnecting"); - if (vc != null) { - double sec = (System.currentTimeMillis() - vc.statStartTime) / 1000.0; - double rate = Math.round(vc.statNumUpdates / sec * 100) / 100.0; - int nRealRects = vc.statNumPixelRects; - int nPseudoRects = vc.statNumTotalRects - vc.statNumPixelRects; - System.out.println("Updates received: " + vc.statNumUpdates + " (" + - nRealRects + " rectangles + " + nPseudoRects + - " pseudo), " + rate + " updates/sec"); - int numRectsOther = nRealRects - vc.statNumRectsTight - - vc.statNumRectsZRLE - vc.statNumRectsHextile - - vc.statNumRectsRaw - vc.statNumRectsCopy; - System.out.println("Rectangles:" + - " Tight=" + vc.statNumRectsTight + - "(JPEG=" + vc.statNumRectsTightJPEG + - ") ZRLE=" + vc.statNumRectsZRLE + - " Hextile=" + vc.statNumRectsHextile + - " Raw=" + vc.statNumRectsRaw + - " CopyRect=" + vc.statNumRectsCopy + - " other=" + numRectsOther); + if (vc != null) { + double sec = (System.currentTimeMillis() - vc.statStartTime) / 1000.0; + double rate = Math.round(vc.statNumUpdates / sec * 100) / 100.0; + int nRealRects = vc.statNumPixelRects; + int nPseudoRects = vc.statNumTotalRects - vc.statNumPixelRects; + System.out.println("Updates received: " + vc.statNumUpdates + " (" + + nRealRects + " rectangles + " + nPseudoRects + + " pseudo), " + rate + " updates/sec"); + int numRectsOther = nRealRects - vc.statNumRectsTight + - vc.statNumRectsZRLE - vc.statNumRectsHextile + - vc.statNumRectsRaw - vc.statNumRectsCopy; + System.out.println("Rectangles:" + " Tight=" + vc.statNumRectsTight + + "(JPEG=" + vc.statNumRectsTightJPEG + ") ZRLE=" + + vc.statNumRectsZRLE + " Hextile=" + + vc.statNumRectsHextile + " Raw=" + vc.statNumRectsRaw + + " CopyRect=" + vc.statNumRectsCopy + " other=" + + numRectsOther); - int raw = vc.statNumBytesDecoded; - int compressed = vc.statNumBytesEncoded; - if (compressed > 0) { - double ratio = Math.round((double)raw / compressed * 1000) / 1000.0; - System.out.println("Pixel data: " + vc.statNumBytesDecoded + - " bytes, " + vc.statNumBytesEncoded + - " compressed, ratio " + ratio); - } - } + int raw = vc.statNumBytesDecoded; + int compressed = vc.statNumBytesEncoded; + if (compressed > 0) { + double ratio = Math.round((double) raw / compressed * 1000) / 1000.0; + System.out.println("Pixel data: " + vc.statNumBytesDecoded + + " bytes, " + vc.statNumBytesEncoded + + " compressed, ratio " + ratio); + } + } - if (rfb != null && !rfb.closed()) - rfb.close(); - options.dispose(); - clipboard.dispose(); - if (rec != null) - rec.dispose(); + if (rfb != null && !rfb.closed()) + rfb.close(); + options.dispose(); + clipboard.dispose(); + if (rec != null) + rec.dispose(); - if (inAnApplet) { - showMessage("Disconnected"); - } else { - System.exit(0); - } - } + if (inAnApplet) { + showMessage("Disconnected"); + } else { + System.exit(0); + } + } + + // + // fatalError() - print out a fatal error message. + // FIXME: Do we really need two versions of the fatalError() method? + // + + synchronized public void fatalError(String str) { + System.out.println(str); - // - // fatalError() - print out a fatal error message. - // FIXME: Do we really need two versions of the fatalError() method? - // + if (inAnApplet) { + // vncContainer null, applet not inited, + // can not present the error to the user. + Thread.currentThread().stop(); + } else { + System.exit(1); + } + } - synchronized public void fatalError(String str) { - System.out.println(str); + synchronized public void fatalError(String str, Exception e) { - if (inAnApplet) { - // vncContainer null, applet not inited, - // can not present the error to the user. - Thread.currentThread().stop(); - } else { - System.exit(1); - } - } + if (rfb != null && rfb.closed()) { + // Not necessary to show error message if the error was caused + // by I/O problems after the rfb.close() method call. + System.out.println("RFB thread finished"); + return; + } + + System.out.println(str); + e.printStackTrace(); - synchronized public void fatalError(String str, Exception e) { - - if (rfb != null && rfb.closed()) { - // Not necessary to show error message if the error was caused - // by I/O problems after the rfb.close() method call. - System.out.println("RFB thread finished"); - return; - } + if (rfb != null) + rfb.close(); - System.out.println(str); - e.printStackTrace(); - - if (rfb != null) - rfb.close(); + if (inAnApplet) { + showMessage(str); + } else { + System.exit(1); + } + } - if (inAnApplet) { - showMessage(str); - } else { - System.exit(1); - } - } + // + // Show message text and optionally "Relogin" and "Close" buttons. + // - // - // Show message text and optionally "Relogin" and "Close" buttons. - // + void showMessage(String msg) { + vncContainer.removeAll(); - void showMessage(String msg) { - vncContainer.removeAll(); + Label errLabel = new Label(msg, Label.CENTER); + errLabel.setFont(new Font("Helvetica", Font.PLAIN, 12)); - Label errLabel = new Label(msg, Label.CENTER); - errLabel.setFont(new Font("Helvetica", Font.PLAIN, 12)); + if (offerRelogin) { - if (offerRelogin) { + Panel gridPanel = new Panel(new GridLayout(0, 1)); + Panel outerPanel = new Panel(new FlowLayout(FlowLayout.LEFT)); + outerPanel.add(gridPanel); + vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 16)); + vncContainer.add(outerPanel); + Panel textPanel = new Panel(new FlowLayout(FlowLayout.CENTER)); + textPanel.add(errLabel); + gridPanel.add(textPanel); + gridPanel.add(new ReloginPanel(this)); + + } else { - Panel gridPanel = new Panel(new GridLayout(0, 1)); - Panel outerPanel = new Panel(new FlowLayout(FlowLayout.LEFT)); - outerPanel.add(gridPanel); - vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 16)); - vncContainer.add(outerPanel); - Panel textPanel = new Panel(new FlowLayout(FlowLayout.CENTER)); - textPanel.add(errLabel); - gridPanel.add(textPanel); - gridPanel.add(new ReloginPanel(this)); + vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 30)); + vncContainer.add(errLabel); + + } - } else { - - vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 30)); - vncContainer.add(errLabel); - - } + if (inSeparateFrame) { + vncFrame.pack(); + } else { + validate(); + } + } - if (inSeparateFrame) { - vncFrame.pack(); - } else { - validate(); - } - } + // + // Stop the applet. + // Main applet thread will terminate on first exception + // after seeing that rfbThread has been set to null. + // - // - // Stop the applet. - // Main applet thread will terminate on first exception - // after seeing that rfbThread has been set to null. - // + public void stop() { + System.out.println("Stopping applet"); + rfbThread = null; + } - public void stop() { - System.out.println("Stopping applet"); - rfbThread = null; - } + // + // This method is called before the applet is destroyed. + // + + public void destroy() { + System.out.println("Destroying applet"); - // - // This method is called before the applet is destroyed. - // - - public void destroy() { - System.out.println("Destroying applet"); + vncContainer.removeAll(); + options.dispose(); + clipboard.dispose(); + if (rec != null) + rec.dispose(); + if (rfb != null && !rfb.closed()) + rfb.close(); + if (inSeparateFrame) + vncFrame.dispose(); + } - vncContainer.removeAll(); - options.dispose(); - clipboard.dispose(); - if (rec != null) - rec.dispose(); - if (rfb != null && !rfb.closed()) - rfb.close(); - if (inSeparateFrame) - vncFrame.dispose(); - } + // + // Start/stop receiving mouse events. + // + + public void enableInput(boolean enable) { + vc.enableInput(enable); + } - // - // Start/stop receiving mouse events. - // - - public void enableInput(boolean enable) { - vc.enableInput(enable); - } + // + // Close application properly on window close event. + // - // - // Close application properly on window close event. - // + public void windowClosing(WindowEvent evt) { + System.out.println("Closing window"); + if (rfb != null) + disconnect(); - public void windowClosing(WindowEvent evt) { - System.out.println("Closing window"); - if (rfb != null) - disconnect(); + vncContainer.hide(); + + if (!inAnApplet) { + System.exit(0); + } + } - vncContainer.hide(); + // + // Ignore window events we're not interested in. + // - if (!inAnApplet) { - System.exit(0); - } - } + public void windowActivated(WindowEvent evt) { + } + + public void windowDeactivated(WindowEvent evt) { + } - // - // Ignore window events we're not interested in. - // + public void windowOpened(WindowEvent evt) { + } + + public void windowClosed(WindowEvent evt) { + } - public void windowActivated(WindowEvent evt) {} - public void windowDeactivated (WindowEvent evt) {} - public void windowOpened(WindowEvent evt) {} - public void windowClosed(WindowEvent evt) {} - public void windowIconified(WindowEvent evt) {} - public void windowDeiconified(WindowEvent evt) {} + public void windowIconified(WindowEvent evt) { + } + + public void windowDeiconified(WindowEvent evt) { + } } diff -r c930d146670f -r 60a87e277536 src/ZlibInStream.java --- a/src/ZlibInStream.java Wed Apr 13 07:48:49 2011 +0900 +++ b/src/ZlibInStream.java Wed Apr 13 08:00:53 2011 +0900 @@ -22,89 +22,95 @@ public class ZlibInStream extends InStream { - static final int defaultBufSize = 16384; + static final int defaultBufSize = 16384; - public ZlibInStream(int bufSize_) { - bufSize = bufSize_; - b = new byte[bufSize]; - ptr = end = ptrOffset = 0; - inflater = new java.util.zip.Inflater(); - } + public ZlibInStream(int bufSize_) { + bufSize = bufSize_; + b = new byte[bufSize]; + ptr = end = ptrOffset = 0; + inflater = new java.util.zip.Inflater(); + } - public ZlibInStream() { this(defaultBufSize); } + public ZlibInStream() { + this(defaultBufSize); + } - public void setUnderlying(InStream is, int bytesIn_) { - underlying = is; - bytesIn = bytesIn_; - ptr = end = 0; - } - - public void reset() throws Exception { - ptr = end = 0; - if (underlying == null) return; + public void setUnderlying(InStream is, int bytesIn_) { + underlying = is; + bytesIn = bytesIn_; + ptr = end = 0; + } - while (bytesIn > 0) { - decompress(); - end = 0; // throw away any data - } - underlying = null; - } + public void reset() throws Exception { + ptr = end = 0; + if (underlying == null) + return; - public int pos() { return ptrOffset + ptr; } + while (bytesIn > 0) { + decompress(); + end = 0; // throw away any data + } + underlying = null; + } - protected int overrun(int itemSize, int nItems) throws Exception { - if (itemSize > bufSize) - throw new Exception("ZlibInStream overrun: max itemSize exceeded"); - if (underlying == null) - throw new Exception("ZlibInStream overrun: no underlying stream"); + public int pos() { + return ptrOffset + ptr; + } - if (end - ptr != 0) - System.arraycopy(b, ptr, b, 0, end - ptr); + protected int overrun(int itemSize, int nItems) throws Exception { + if (itemSize > bufSize) + throw new Exception("ZlibInStream overrun: max itemSize exceeded"); + if (underlying == null) + throw new Exception("ZlibInStream overrun: no underlying stream"); - ptrOffset += ptr; - end -= ptr; - ptr = 0; + if (end - ptr != 0) + System.arraycopy(b, ptr, b, 0, end - ptr); - while (end < itemSize) { - decompress(); - } + ptrOffset += ptr; + end -= ptr; + ptr = 0; - if (itemSize * nItems > end) - nItems = end / itemSize; - - return nItems; - } + while (end < itemSize) { + decompress(); + } - // decompress() calls the decompressor once. Note that this won't - // necessarily generate any output data - it may just consume some input - // data. Returns false if wait is false and we would block on the underlying - // stream. + if (itemSize * nItems > end) + nItems = end / itemSize; - private void decompress() throws Exception { - try { - underlying.check(1); - int avail_in = underlying.getend() - underlying.getptr(); - if (avail_in > bytesIn) - avail_in = bytesIn; + return nItems; + } + + // decompress() calls the decompressor once. Note that this won't + // necessarily generate any output data - it may just consume some input + // data. Returns false if wait is false and we would block on the underlying + // stream. - if (inflater.needsInput()) { - inflater.setInput(underlying.getbuf(), underlying.getptr(), avail_in); - } + private void decompress() throws Exception { + try { + underlying.check(1); + int avail_in = underlying.getend() - underlying.getptr(); + if (avail_in > bytesIn) + avail_in = bytesIn; + + if (inflater.needsInput()) { + inflater.setInput(underlying.getbuf(), underlying.getptr(), + avail_in); + } - int n = inflater.inflate(b, end, bufSize - end); - end += n; - if (inflater.needsInput()) { - bytesIn -= avail_in; - underlying.setptr(underlying.getptr() + avail_in); - } - } catch (java.util.zip.DataFormatException e) { - throw new Exception("ZlibInStream: inflate failed"); - } - } + int n = inflater.inflate(b, end, bufSize - end); + end += n; + if (inflater.needsInput()) { + bytesIn -= avail_in; + underlying.setptr(underlying.getptr() + avail_in); + } + } catch (java.util.zip.DataFormatException e) { + throw new Exception("ZlibInStream: inflate failed"); + } + } - private InStream underlying; - private int bufSize; - private int ptrOffset; - private java.util.zip.Inflater inflater; - private int bytesIn; + private InStream underlying; + private int bufSize; + private int ptrOffset; + private java.util.zip.Inflater inflater; + private int bytesIn; }