From bcaadbe7ae3dcb18375bcb878a7794058b96aeee Mon Sep 17 00:00:00 2001 From: "Brian P. Hinz" Date: Sat, 10 Dec 2016 19:14:55 -0500 Subject: [PATCH] Method cleanup. Sync various methods and filenames with C++ versions --- java/com/tigervnc/rdr/InStream.java | 14 -- java/com/tigervnc/rdr/ZlibInStream.java | 12 ++ java/com/tigervnc/rfb/CConnection.java | 213 +++++++++++++++--------- java/com/tigervnc/rfb/CMsgHandler.java | 11 +- java/com/tigervnc/rfb/CMsgReader.java | 194 ++++++++++++++++----- java/com/tigervnc/rfb/CMsgWriter.java | 112 +++++++++++-- java/com/tigervnc/rfb/ConnParams.java | 161 ++++++++++-------- java/com/tigervnc/rfb/ScreenSet.java | 14 +- 8 files changed, 500 insertions(+), 231 deletions(-) diff --git a/java/com/tigervnc/rdr/InStream.java b/java/com/tigervnc/rdr/InStream.java index f713d738..0906d326 100644 --- a/java/com/tigervnc/rdr/InStream.java +++ b/java/com/tigervnc/rdr/InStream.java @@ -159,20 +159,6 @@ abstract public class InStream { } } - public final int readCompactLength() { - int b = readU8(); - int result = b & 0x7F; - if ((b & 0x80) != 0) { - b = readU8(); - result |= (b & 0x7F) << 7; - if ((b & 0x80) != 0) { - b = readU8(); - result |= (b & 0xFF) << 14; - } - } - return result; - } - // pos() returns the position in the stream. abstract public int pos(); diff --git a/java/com/tigervnc/rdr/ZlibInStream.java b/java/com/tigervnc/rdr/ZlibInStream.java index 103bb646..151633ec 100644 --- a/java/com/tigervnc/rdr/ZlibInStream.java +++ b/java/com/tigervnc/rdr/ZlibInStream.java @@ -62,6 +62,18 @@ public class ZlibInStream extends InStream { ptr = end = start; } + public void removeUnderlying() + { + ptr = end = start; + if (underlying == null) return; + + while (bytesIn > 0) { + decompress(true); + end = start; // throw away any data + } + underlying = null; + } + public int pos() { return offset + ptr - start; diff --git a/java/com/tigervnc/rfb/CConnection.java b/java/com/tigervnc/rfb/CConnection.java index 483d1f84..0b38aeaf 100644 --- a/java/com/tigervnc/rfb/CConnection.java +++ b/java/com/tigervnc/rfb/CConnection.java @@ -19,7 +19,11 @@ package com.tigervnc.rfb; +import java.awt.color.*; +import java.awt.image.*; +import java.nio.*; import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; import com.tigervnc.network.*; import com.tigervnc.rdr.*; @@ -28,21 +32,86 @@ abstract public class CConnection extends CMsgHandler { public CConnection() { - csecurity = null; is = null; os = null; reader_ = null; - writer_ = null; shared = false; + super(); + csecurity = null; is = null; os = null; reader_ = null; writer_ = null; + shared = false; state_ = RFBSTATE_UNINITIALISED; useProtocol3_3 = false; + framebuffer = null; decoder = new DecodeManager(this); security = new SecurityClient(); } - // deleteReaderAndWriter() deletes the reader and writer associated with - // this connection. This may be useful if you want to delete the streams - // before deleting the SConnection to make sure that no attempt by the - // SConnection is made to read or write. - // XXX Do we really need this at all??? - public void deleteReaderAndWriter() + // Methods to initialise the connection + + // setServerName() is used to provide a unique(ish) name for the server to + // which we are connected. This might be the result of getPeerEndpoint on + // a TcpSocket, for example, or a host specified by DNS name & port. + // The serverName is used when verifying the Identity of a host (see RA2). + public final void setServerName(String name) { + serverName = name; + } + + // setShared sets the value of the shared flag which will be sent to the + // server upon initialisation. + public final void setShared(boolean s) { shared = s; } + + // setProtocol3_3 configures whether or not the CConnection should + // only ever support protocol version 3.3 + public final void setProtocol3_3(boolean s) { useProtocol3_3 = s; } + + // setStreams() sets the streams to be used for the connection. These must + // be set before initialiseProtocol() and processMsg() are called. The + // CSecurity object may call setStreams() again to provide alternative + // streams over which the RFB protocol is sent (i.e. encrypting/decrypting + // streams). Ownership of the streams remains with the caller + // (i.e. SConnection will not delete them). + public final void setStreams(InStream is_, OutStream os_) + { + is = is_; + os = os_; + } + + // setFramebuffer configures the PixelBuffer that the CConnection + // should render all pixel data in to. Note that the CConnection + // takes ownership of the PixelBuffer and it must not be deleted by + // anyone else. Call setFramebuffer again with NULL or a different + // PixelBuffer to delete the previous one. + public void setFramebuffer(ModifiablePixelBuffer fb) { - reader_ = null; - writer_ = null; + decoder.flush(); + + if ((framebuffer != null) && (fb != null)) { + Rect rect = new Rect(); + + Raster data; + + byte[] black = new byte[4]; + + // Copy still valid area + + rect.setXYWH(0, 0, + Math.min(fb.width(), framebuffer.width()), + Math.min(fb.height(), framebuffer.height())); + data = framebuffer.getBuffer(rect); + fb.imageRect(framebuffer.getPF(), rect, data); + + // Black out any new areas + + if (fb.width() > framebuffer.width()) { + rect.setXYWH(framebuffer.width(), 0, + fb.width() - framebuffer.width(), + fb.height()); + fb.fillRect(rect, black); + } + + if (fb.height() > framebuffer.height()) { + rect.setXYWH(0, framebuffer.height(), + fb.width(), + fb.height() - framebuffer.height()); + fb.fillRect(rect, black); + } + } + + framebuffer = fb; } // initialiseProtocol() should be called once the streams and security @@ -75,11 +144,12 @@ abstract public class CConnection extends CMsgHandler { private void processVersionMsg() { vlog.debug("reading protocol version"); - if (!cp.readVersion(is)) { + AtomicBoolean done = new AtomicBoolean(); + if (!cp.readVersion(is, done)) { state_ = RFBSTATE_INVALID; throw new Exception("reading version failed: not an RFB server?"); } - if (!cp.done) return; + if (!done.get()) return; vlog.info("Server supports RFB protocol version " +cp.majorVersion+"."+ cp.minorVersion); @@ -241,52 +311,30 @@ abstract public class CConnection extends CMsgHandler { private void securityCompleted() { state_ = RFBSTATE_INITIALISATION; - reader_ = new CMsgReaderV3(this, is); - writer_ = new CMsgWriterV3(cp, os); + reader_ = new CMsgReader(this, is); + writer_ = new CMsgWriter(cp, os); vlog.debug("Authentication success!"); authSuccess(); writer_.writeClientInit(shared); } - // Methods to initialise the connection - - // setServerName() is used to provide a unique(ish) name for the server to - // which we are connected. This might be the result of getPeerEndpoint on - // a TcpSocket, for example, or a host specified by DNS name & port. - // The serverName is used when verifying the Identity of a host (see RA2). - public final void setServerName(String name) { - serverName = name; - } - - // setStreams() sets the streams to be used for the connection. These must - // be set before initialiseProtocol() and processMsg() are called. The - // CSecurity object may call setStreams() again to provide alternative - // streams over which the RFB protocol is sent (i.e. encrypting/decrypting - // streams). Ownership of the streams remains with the caller - // (i.e. SConnection will not delete them). - public final void setStreams(InStream is_, OutStream os_) - { - is = is_; - os = os_; - } + // Methods to be overridden in a derived class - // setShared sets the value of the shared flag which will be sent to the - // server upon initialisation. - public final void setShared(boolean s) { shared = s; } + // Note: These must be called by any deriving classes - // setProtocol3_3 configures whether or not the CConnection should - // only ever support protocol version 3.3 - public final void setProtocol3_3(boolean s) { useProtocol3_3 = s; } + public void setDesktopSize(int w, int h) { + decoder.flush(); - public void setServerPort(int port) { - serverPort = port; + super.setDesktopSize(w,h); } - public void initSecTypes() { - nSecTypes = 0; - } + public void setExtendedDesktopSize(int reason, + int result, int w, int h, + ScreenSet layout) { + decoder.flush(); - // Methods to be overridden in a derived class + super.setExtendedDesktopSize(reason, result, w, h, layout); + } // getIdVerifier() returns the identity verifier associated with the connection. // Ownership of the IdentityVerifier is retained by the CConnection instance. @@ -299,11 +347,20 @@ abstract public class CConnection extends CMsgHandler { public void framebufferUpdateEnd() { + decoder.flush(); + super.framebufferUpdateEnd(); } + public void dataRect(Rect r, int encoding) + { + decoder.decodeRect(r, encoding, framebuffer); + } + // authSuccess() is called when authentication has succeeded. - public void authSuccess() {} + public void authSuccess() + { + } // serverInit() is called when the ServerInit message is received. The // derived class must call on to CConnection::serverInit(). @@ -313,34 +370,17 @@ abstract public class CConnection extends CMsgHandler { vlog.debug("initialisation done"); } - // getCSecurity() gets the CSecurity object for the given type. The type - // is guaranteed to be one of the secTypes passed in to addSecType(). The - // CSecurity object's destroy() method will be called by the CConnection - // from its destructor. - //abstract public CSecurity getCSecurity(int secType); - - // getCurrentCSecurity() gets the CSecurity instance used for this - // connection. - //public CSecurity getCurrentCSecurity() { return security; } - - // setClientSecTypeOrder() determines whether the client should obey the - // server's security type preference, by picking the first server security - // type that the client supports, or whether it should pick the first type - // that the server supports, from the client-supported list of types. - public void setClientSecTypeOrder( boolean csto ) { - clientSecTypeOrder = csto; - } - // Other methods - public CMsgReaderV3 reader() { return reader_; } - public CMsgWriterV3 writer() { return writer_; } + public CMsgReader reader() { return reader_; } + public CMsgWriter writer() { return writer_; } public InStream getInStream() { return is; } public OutStream getOutStream() { return os; } + // Access method used by SSecurity implementations that can verify servers' + // Identities, to determine the unique(ish) name of the server. public String getServerName() { return serverName; } - public int getServerPort() { return serverPort; } public static final int RFBSTATE_UNINITIALISED = 0; public static final int RFBSTATE_PROTOCOL_VERSION = 1; @@ -353,7 +393,17 @@ abstract public class CConnection extends CMsgHandler { public int state() { return state_; } - protected final void setState(int s) { state_ = s; } + public int getServerPort() { return serverPort; } + public void setServerPort(int port) { + serverPort = port; + } + + protected void setState(int s) { state_ = s; } + + protected void setReader(CMsgReader r) { reader_ = r; } + protected void setWriter(CMsgWriter w) { writer_ = w; } + + protected ModifiablePixelBuffer getFramebuffer() { return framebuffer; } public void fence(int flags, int len, byte[] data) { @@ -383,20 +433,27 @@ abstract public class CConnection extends CMsgHandler { throw new AuthFailureException(reason); } - InStream is; - OutStream os; - CMsgReaderV3 reader_; - CMsgWriterV3 writer_; - boolean shared; + private InStream is; + private OutStream os; + private CMsgReader reader_; + private CMsgWriter writer_; + private boolean deleteStreamsWhenDone; + private boolean shared; + private int state_ = RFBSTATE_UNINITIALISED; + + private String serverName; + + private boolean useProtocol3_3; + + protected ModifiablePixelBuffer framebuffer; + private DecodeManager decoder; + public CSecurity csecurity; public SecurityClient security; public static final int maxSecTypes = 8; int nSecTypes; int[] secTypes; - int state_ = RFBSTATE_UNINITIALISED; - String serverName; int serverPort; - boolean useProtocol3_3; boolean clientSecTypeOrder; static LogWriter vlog = new LogWriter("CConnection"); diff --git a/java/com/tigervnc/rfb/CMsgHandler.java b/java/com/tigervnc/rfb/CMsgHandler.java index dd9767e1..99405983 100644 --- a/java/com/tigervnc/rfb/CMsgHandler.java +++ b/java/com/tigervnc/rfb/CMsgHandler.java @@ -78,25 +78,18 @@ abstract public class CMsgHandler { String x509subject) {} public void setCursor(int width, int height, Point hotspot, - int[] data, byte[] mask) {} + byte[] data, byte[] mask) {} public void serverInit() {} public void framebufferUpdateStart() {} public void framebufferUpdateEnd() {} - public void beginRect(Rect r, int encoding) {} - public void endRect(Rect r, int encoding) {} + public void dataRect(Rect r, int encoding) {} public void setColourMapEntries(int firstColour, int nColours, int[] rgbs) { } public void bell() {} public void serverCutText(String str, int len) {} - public void fillRect(Rect r, int pix) {} - public void imageRect(Rect r, Object pixels) {} - public void copyRect(Rect r, int srcX, int srcY) {} - - abstract public PixelFormat getPreferredPF(); - public ConnParams cp; static LogWriter vlog = new LogWriter("CMsgHandler"); diff --git a/java/com/tigervnc/rfb/CMsgReader.java b/java/com/tigervnc/rfb/CMsgReader.java index a93324ca..e7b4deb8 100644 --- a/java/com/tigervnc/rfb/CMsgReader.java +++ b/java/com/tigervnc/rfb/CMsgReader.java @@ -28,7 +28,7 @@ import java.nio.CharBuffer; import java.nio.charset.Charset; import com.tigervnc.rdr.*; -abstract public class CMsgReader { +public class CMsgReader { protected CMsgReader(CMsgHandler handler_, InStream is_) { @@ -37,7 +37,82 @@ abstract public class CMsgReader { is = is_; imageBuf = null; imageBufSize = 0; - decoders = new Decoder[Encodings.encodingMax+1]; + nUpdateRectsLeft = 0; + } + + public void readServerInit() + { + int width = is.readU16(); + int height = is.readU16(); + handler.setDesktopSize(width, height); + PixelFormat pf = new PixelFormat(); + pf.read(is); + handler.setPixelFormat(pf); + String name = is.readString(); + handler.setName(name); + handler.serverInit(); + } + + public void readMsg() + { + if (nUpdateRectsLeft == 0) { + int type = is.readU8(); + + switch (type) { + case MsgTypes.msgTypeSetColourMapEntries: + readSetColourMapEntries(); + break; + case MsgTypes.msgTypeBell: + readBell(); + break; + case MsgTypes.msgTypeServerCutText: + readServerCutText(); + break; + case MsgTypes.msgTypeFramebufferUpdate: + readFramebufferUpdate(); + break; + case MsgTypes.msgTypeServerFence: + readFence(); + break; + case MsgTypes.msgTypeEndOfContinuousUpdates: + readEndOfContinuousUpdates(); + break; + default: + //fprintf(stderr, "unknown message type %d\n", type); + throw new Exception("unknown message type"); + } + } else { + int x = is.readU16(); + int y = is.readU16(); + int w = is.readU16(); + int h = is.readU16(); + int encoding = is.readS32(); + + switch (encoding) { + case Encodings.pseudoEncodingLastRect: + nUpdateRectsLeft = 1; // this rectangle is the last one + break; + case Encodings.pseudoEncodingCursor: + readSetCursor(w, h, new Point(x,y)); + break; + case Encodings.pseudoEncodingDesktopName: + readSetDesktopName(x, y, w, h); + break; + case Encodings.pseudoEncodingDesktopSize: + handler.setDesktopSize(w, h); + break; + case Encodings.pseudoEncodingExtendedDesktopSize: + readExtendedDesktopSize(x, y, w, h); + break; + default: + readRect(new Rect(x, y, x+w, y+h), encoding); + break; + }; + + nUpdateRectsLeft--; + if (nUpdateRectsLeft == 0) + handler.framebufferUpdateEnd(); + } } protected void readSetColourMapEntries() @@ -72,6 +147,43 @@ abstract public class CMsgReader { handler.serverCutText(chars.toString(), len); } + protected void readFence() + { + int flags; + int len; + byte[] data = new byte[64]; + + is.skip(3); + + flags = is.readU32(); + + len = is.readU8(); + if (len > data.length) { + System.out.println("Ignoring fence with too large payload\n"); + is.skip(len); + return; + } + + is.readBytes(data, 0, len); + + handler.fence(flags, len, data); + } + + protected void readEndOfContinuousUpdates() + { + handler.endOfContinuousUpdates(); + } + + protected void readFramebufferUpdate() + { + is.skip(1); + nUpdateRectsLeft = is.readU16(); + handler.framebufferUpdateStart(); + } + + + + /* protected void readFramebufferUpdateStart() { handler.framebufferUpdateStart(); @@ -81,6 +193,7 @@ abstract public class CMsgReader { { handler.framebufferUpdateEnd(); } + */ protected void readRect(Rect r, int encoding) { @@ -94,43 +207,56 @@ abstract public class CMsgReader { if (r.is_empty()) vlog.error("Ignoring zero size rect"); - handler.beginRect(r, encoding); + handler.dataRect(r, encoding); + } - if (encoding == Encodings.encodingCopyRect) { - readCopyRect(r); - } else { + protected void readSetCursor(int width, int height, Point hotspot) + { + int data_len = width * height * (handler.cp.pf().bpp/8); + int mask_len = ((width+7)/8) * height; + byte[] data = new byte[data_len]; + byte[] mask = new byte[mask_len]; - if (decoders[encoding] == null) { - decoders[encoding] = Decoder.createDecoder(encoding, this); - if (decoders[encoding] == null) { - vlog.error("Unknown rect encoding "+encoding); - throw new Exception("Unknown rect encoding"); - } - } - decoders[encoding].readRect(r, handler); - } + is.readBytes(data, 0, data_len); + is.readBytes(mask, 0, mask_len); - handler.endRect(r, encoding); + handler.setCursor(width, height, hotspot, data, mask); } - protected void readCopyRect(Rect r) + protected void readSetDesktopName(int x, int y, int w, int h) { - int srcX = is.readU16(); - int srcY = is.readU16(); - handler.copyRect(r, srcX, srcY); + String name = is.readString(); + + if (x != 0 || y != 0 || w != 0 || h != 0) { + vlog.error("Ignoring DesktopName rect with non-zero position/size"); + } else { + handler.setName(name); + } + } - protected void readSetCursor(int width, int height, Point hotspot) + protected void readExtendedDesktopSize(int x, int y, int w, int h) { - int data_len = width * height; - int mask_len = ((width+7)/8) * height; - int[] data = new int[data_len]; - byte[] mask = new byte[mask_len]; + int screens, i; + int id, flags; + int sx, sy, sw, sh; + ScreenSet layout = new ScreenSet(); - is.readPixels(data, data_len, (handler.cp.pf().bpp/8), handler.cp.pf().bigEndian); - is.readBytes(mask, 0, mask_len); + screens = is.readU8(); + is.skip(3); - handler.setCursor(width, height, hotspot, data, mask); + for (i = 0;i < screens;i++) { + id = is.readU32(); + sx = is.readU16(); + sy = is.readU16(); + sw = is.readU16(); + sh = is.readU16(); + flags = is.readU32(); + + layout.add_screen(new Screen(id, sx, sy, sw, sh, flags)); + } + + handler.setExtendedDesktopSize(x, y, w, h, layout); } public int[] getImageBuf(int required) { return getImageBuf(required, 0, 0); } @@ -154,23 +280,13 @@ abstract public class CMsgReader { return imageBuf; } - public final int bpp() - { - return handler.cp.pf().bpp; - } - - abstract public void readServerInit(); - - // readMsg() reads a message, calling the handler as appropriate. - abstract public void readMsg(); - public InStream getInStream() { return is; } public int imageBufIdealSize; protected CMsgHandler handler; protected InStream is; - protected Decoder[] decoders; + protected int nUpdateRectsLeft; protected int[] imageBuf; protected int imageBufSize; diff --git a/java/com/tigervnc/rfb/CMsgWriter.java b/java/com/tigervnc/rfb/CMsgWriter.java index 0d3dccf9..bdf50c28 100644 --- a/java/com/tigervnc/rfb/CMsgWriter.java +++ b/java/com/tigervnc/rfb/CMsgWriter.java @@ -20,13 +20,23 @@ package com.tigervnc.rfb; +import com.tigervnc.rdr.*; import java.nio.ByteBuffer; import java.nio.charset.Charset; -import com.tigervnc.rdr.*; +import java.util.Iterator; -abstract public class CMsgWriter { +public class CMsgWriter { - abstract public void writeClientInit(boolean shared); + protected CMsgWriter(ConnParams cp_, OutStream os_) + { + cp = cp_; + os = os_; + } + + synchronized public void writeClientInit(boolean shared) { + os.writeU8(shared?1:0); + endMsg(); + } synchronized public void writeSetPixelFormat(PixelFormat pf) { @@ -53,6 +63,7 @@ abstract public class CMsgWriter { { int nEncodings = 0; int[] encodings = new int[Encodings.encodingMax+3]; + if (cp.supportsLocalCursor) encodings[nEncodings++] = Encodings.pseudoEncodingCursor; if (cp.supportsDesktopResize) @@ -68,7 +79,6 @@ abstract public class CMsgWriter { encodings[nEncodings++] = Encodings.pseudoEncodingContinuousUpdates; encodings[nEncodings++] = Encodings.pseudoEncodingFence; - if (Decoder.supported(preferredEncoding)) { encodings[nEncodings++] = preferredEncoding; } @@ -98,9 +108,11 @@ abstract public class CMsgWriter { // Remaining encodings for (int i = Encodings.encodingMax; i >= 0; i--) { switch (i) { + case Encodings.encodingCopyRect: case Encodings.encodingTight: case Encodings.encodingZRLE: case Encodings.encodingHextile: + /* These have already been sent earlier */ break; default: if ((i != preferredEncoding) && Decoder.supported(i)) @@ -108,15 +120,44 @@ abstract public class CMsgWriter { } } - encodings[nEncodings++] = Encodings.pseudoEncodingLastRect; - if (cp.customCompressLevel && cp.compressLevel >= 0 && cp.compressLevel <= 9) - encodings[nEncodings++] = Encodings.pseudoEncodingCompressLevel0 + cp.compressLevel; - if (!cp.noJpeg && cp.qualityLevel >= 0 && cp.qualityLevel <= 9) - encodings[nEncodings++] = Encodings.pseudoEncodingQualityLevel0 + cp.qualityLevel; + if (cp.compressLevel >= 0 && cp.compressLevel <= 9) + encodings[nEncodings++] = + Encodings.pseudoEncodingCompressLevel0 + cp.compressLevel; + if (cp.qualityLevel >= 0 && cp.qualityLevel <= 9) + encodings[nEncodings++] = + Encodings.pseudoEncodingQualityLevel0 + cp.qualityLevel; writeSetEncodings(nEncodings, encodings); } + synchronized public void writeSetDesktopSize(int width, int height, + ScreenSet layout) + { + if (!cp.supportsSetDesktopSize) + throw new Exception("Server does not support SetDesktopSize"); + + startMsg(MsgTypes.msgTypeSetDesktopSize); + os.pad(1); + + os.writeU16(width); + os.writeU16(height); + + os.writeU8(layout.num_screens()); + os.pad(1); + + for (Iterator iter = layout.screens.iterator(); iter.hasNext(); ) { + Screen refScreen = (Screen)iter.next(); + os.writeU32(refScreen.id); + os.writeU16(refScreen.dimensions.tl.x); + os.writeU16(refScreen.dimensions.tl.y); + os.writeU16(refScreen.dimensions.width()); + os.writeU16(refScreen.dimensions.height()); + os.writeU32(refScreen.flags); + } + + endMsg(); + } + synchronized public void writeFramebufferUpdateRequest(Rect r, boolean incremental) { startMsg(MsgTypes.msgTypeFramebufferUpdateRequest); @@ -128,6 +169,44 @@ abstract public class CMsgWriter { endMsg(); } + synchronized public void writeEnableContinuousUpdates(boolean enable, + int x, int y, int w, int h) + { + if (!cp.supportsContinuousUpdates) + throw new Exception("Server does not support continuous updates"); + + startMsg(MsgTypes.msgTypeEnableContinuousUpdates); + + os.writeU8((enable?1:0)); + + os.writeU16(x); + os.writeU16(y); + os.writeU16(w); + os.writeU16(h); + + endMsg(); + } + + synchronized public void writeFence(int flags, int len, byte[] data) + { + if (!cp.supportsFence) + throw new Exception("Server does not support fences"); + if (len > 64) + throw new Exception("Too large fence payload"); + if ((flags & ~fenceTypes.fenceFlagsSupported) != 0) + throw new Exception("Unknown fence flags"); + + startMsg(MsgTypes.msgTypeClientFence); + os.pad(3); + + os.writeU32(flags); + + os.writeU8(len); + os.writeBytes(data, 0, len); + + endMsg(); + } + synchronized public void writeKeyEvent(int key, boolean down) { startMsg(MsgTypes.msgTypeKeyEvent); @@ -163,17 +242,14 @@ abstract public class CMsgWriter { endMsg(); } - abstract public void startMsg(int type); - abstract public void endMsg(); - - synchronized public void setOutStream(OutStream os_) { os = os_; } - - ConnParams getConnParams() { return cp; } - OutStream getOutStream() { return os; } + synchronized public void startMsg(int type) { + os.writeU8(type); + } - protected CMsgWriter(ConnParams cp_, OutStream os_) {cp = cp_; os = os_;} + synchronized public void endMsg() { + os.flush(); + } ConnParams cp; OutStream os; - static LogWriter vlog = new LogWriter("CMsgWriter"); } diff --git a/java/com/tigervnc/rfb/ConnParams.java b/java/com/tigervnc/rfb/ConnParams.java index f1f53958..fe52770f 100644 --- a/java/com/tigervnc/rfb/ConnParams.java +++ b/java/com/tigervnc/rfb/ConnParams.java @@ -1,6 +1,6 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. * Copyright (C) 2011 D. R. Commander. All Rights Reserved. - * Copyright (C) 2012 Brian P. Hinz + * Copyright (C) 2012-2016 Brian P. Hinz * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,11 +21,21 @@ package com.tigervnc.rfb; import com.tigervnc.rdr.*; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; public class ConnParams { - static LogWriter vlog = new LogWriter("ConnParams"); - public ConnParams() { + private static final int subsampleUndefined = -1; + private static final int subsampleNone = 0; + private static final int subsampleGray = 1; + private static final int subsample2X = 2; + private static final int subsample4X = 3; + private static final int subsample8X = 4; + private static final int subsample16X = 5; + + public ConnParams() + { majorVersion = 0; minorVersion = 0; width = 0; height = 0; useCopyRect = false; supportsLocalCursor = false; supportsLocalXCursor = false; @@ -34,19 +44,17 @@ public class ConnParams { supportsSetDesktopSize = false; supportsFence = false; supportsContinuousUpdates = false; supportsClientRedirect = false; - customCompressLevel = false; compressLevel = 6; - noJpeg = false; qualityLevel = -1; fineQualityLevel = -1; - subsampling = "SUBSAMP_UNDEFINED"; - name_ = null; nEncodings_ = 0; encodings_ = null; - currentEncoding_ = Encodings.encodingRaw; verStrPos = 0; + compressLevel = 6; qualityLevel = -1; fineQualityLevel = -1; + subsampling = subsampleUndefined; name_ = null; verStrPos = 0; + + encodings_ = new ArrayList(); screenLayout = new ScreenSet(); setName(""); } - public boolean readVersion(InStream is) + public boolean readVersion(InStream is, AtomicBoolean done) { - done = false; if (verStrPos >= 12) return false; verStr = new StringBuilder(13); while (is.checkNoWait(1) && verStrPos < 12) { @@ -54,10 +62,10 @@ public class ConnParams { } if (verStrPos < 12) { - done = false; + done.set(false); return true; } - done = true; + done.set(true); verStr.insert(12,'0'); verStrPos = 0; if (verStr.toString().matches("RFB \\d{3}\\.\\d{3}\\n0")) { @@ -68,7 +76,8 @@ public class ConnParams { return false; } - public void writeVersion(OutStream os) { + public void writeVersion(OutStream os) + { String str = String.format("RFB %03d.%03d\n", majorVersion, minorVersion); os.writeBytes(str.getBytes(), 0, 12); os.flush(); @@ -97,10 +106,11 @@ public class ConnParams { public PixelFormat pf() { return pf_; } public void setPF(PixelFormat pf) { + pf_ = pf; - if (pf.bpp != 8 && pf.bpp != 16 && pf.bpp != 32) { + + if (pf.bpp != 8 && pf.bpp != 16 && pf.bpp != 32) throw new Exception("setPF: not 8, 16 or 32 bpp?"); - } } public String name() { return name_; } @@ -109,80 +119,96 @@ public class ConnParams { name_ = name; } - public int currentEncoding() { return currentEncoding_; } - public int nEncodings() { return nEncodings_; } - public int[] encodings() { return encodings_; } + public boolean supportsEncoding(int encoding) + { + return encodings_.indexOf(encoding) != -1; + } + public void setEncodings(int nEncodings, int[] encodings) { - if (nEncodings > nEncodings_) { - encodings_ = new int[nEncodings]; - } - nEncodings_ = nEncodings; useCopyRect = false; supportsLocalCursor = false; supportsDesktopResize = false; supportsExtendedDesktopSize = false; supportsLocalXCursor = false; supportsLastRect = false; - customCompressLevel = false; compressLevel = -1; - noJpeg = true; qualityLevel = -1; fineQualityLevel = -1; - subsampling = "SUBSAMP_UNDEFINED"; - currentEncoding_ = Encodings.encodingRaw; + subsampling = subsampleUndefined; - for (int i = nEncodings-1; i >= 0; i--) { - encodings_[i] = encodings[i]; + encodings_.clear(); + encodings_.add(Encodings.encodingRaw); - if (encodings[i] == Encodings.encodingCopyRect) + for (int i = nEncodings-1; i >= 0; i--) { + switch (encodings[i]) { + case Encodings.encodingCopyRect: useCopyRect = true; - else if (encodings[i] == Encodings.pseudoEncodingCursor) + break; + case Encodings.pseudoEncodingCursor: supportsLocalCursor = true; - else if (encodings[i] == Encodings.pseudoEncodingXCursor) + break; + case Encodings.pseudoEncodingXCursor: supportsLocalXCursor = true; - else if (encodings[i] == Encodings.pseudoEncodingDesktopSize) + break; + case Encodings.pseudoEncodingDesktopSize: supportsDesktopResize = true; - else if (encodings[i] == Encodings.pseudoEncodingExtendedDesktopSize) + break; + case Encodings.pseudoEncodingExtendedDesktopSize: supportsExtendedDesktopSize = true; - else if (encodings[i] == Encodings.pseudoEncodingDesktopName) + break; + case Encodings.pseudoEncodingDesktopName: supportsDesktopRename = true; - else if (encodings[i] == Encodings.pseudoEncodingLastRect) + break; + case Encodings.pseudoEncodingLastRect: supportsLastRect = true; - else if (encodings[i] == Encodings.pseudoEncodingFence) + break; + case Encodings.pseudoEncodingFence: supportsFence = true; - else if (encodings[i] == Encodings.pseudoEncodingContinuousUpdates) + break; + case Encodings.pseudoEncodingContinuousUpdates: supportsContinuousUpdates = true; - else if (encodings[i] == Encodings.pseudoEncodingClientRedirect) + break; + case Encodings.pseudoEncodingClientRedirect: supportsClientRedirect = true; - else if (encodings[i] >= Encodings.pseudoEncodingCompressLevel0 && - encodings[i] <= Encodings.pseudoEncodingCompressLevel9) { - customCompressLevel = true; + break; + case Encodings.pseudoEncodingSubsamp1X: + subsampling = subsampleNone; + break; + case Encodings.pseudoEncodingSubsampGray: + subsampling = subsampleGray; + break; + case Encodings.pseudoEncodingSubsamp2X: + subsampling = subsample2X; + break; + case Encodings.pseudoEncodingSubsamp4X: + subsampling = subsample4X; + break; + case Encodings.pseudoEncodingSubsamp8X: + subsampling = subsample8X; + break; + case Encodings.pseudoEncodingSubsamp16X: + subsampling = subsample16X; + break; + } + + if (encodings[i] >= Encodings.pseudoEncodingCompressLevel0 && + encodings[i] <= Encodings.pseudoEncodingCompressLevel9) compressLevel = encodings[i] - Encodings.pseudoEncodingCompressLevel0; - } else if (encodings[i] >= Encodings.pseudoEncodingQualityLevel0 && - encodings[i] <= Encodings.pseudoEncodingQualityLevel9) { - noJpeg = false; + + if (encodings[i] >= Encodings.pseudoEncodingQualityLevel0 && + encodings[i] <= Encodings.pseudoEncodingQualityLevel9) qualityLevel = encodings[i] - Encodings.pseudoEncodingQualityLevel0; - } else if (encodings[i] <= Encodings.encodingMax && - Encoder.supported(encodings[i])) - currentEncoding_ = encodings[i]; - } - // If the TurboVNC fine quality/subsampling encodings exist, let them - // override the coarse TightVNC quality level - for (int i = nEncodings-1; i >= 0; i--) { - if (encodings[i] >= Encodings.pseudoEncodingFineQualityLevel0 + 1 && - encodings[i] <= Encodings.pseudoEncodingFineQualityLevel100) { - noJpeg = false; + if (encodings[i] >= Encodings.pseudoEncodingFineQualityLevel0 && + encodings[i] <= Encodings.pseudoEncodingFineQualityLevel100) fineQualityLevel = encodings[i] - Encodings.pseudoEncodingFineQualityLevel0; - } else if (encodings[i] >= Encodings.pseudoEncodingSubsamp1X && - encodings[i] <= Encodings.pseudoEncodingSubsampGray) { - noJpeg = false; - subsampling = JpegCompressor.subsamplingName(encodings[i] - Encodings.pseudoEncodingSubsamp1X); - } + + if (encodings[i] > 0) + encodings_.add(encodings[i]); } } - public boolean done; + public boolean useCopyRect; public boolean supportsLocalCursor; @@ -190,25 +216,22 @@ public class ConnParams { public boolean supportsDesktopResize; public boolean supportsExtendedDesktopSize; public boolean supportsDesktopRename; - public boolean supportsClientRedirect; - public boolean supportsFence; - public boolean supportsContinuousUpdates; public boolean supportsLastRect; + public boolean supportsClientRedirect; public boolean supportsSetDesktopSize; + public boolean supportsFence; + public boolean supportsContinuousUpdates; - public boolean customCompressLevel; public int compressLevel; - public boolean noJpeg; public int qualityLevel; public int fineQualityLevel; - public String subsampling; + public int subsampling; private PixelFormat pf_; private String name_; - private int nEncodings_; - private int[] encodings_; - private int currentEncoding_; + private Cursor cursor_; + private ArrayList encodings_; private StringBuilder verStr; private int verStrPos; } diff --git a/java/com/tigervnc/rfb/ScreenSet.java b/java/com/tigervnc/rfb/ScreenSet.java index a14f561d..173dd101 100644 --- a/java/com/tigervnc/rfb/ScreenSet.java +++ b/java/com/tigervnc/rfb/ScreenSet.java @@ -31,13 +31,18 @@ public class ScreenSet { screens = new ArrayList(); } + public final ListIterator begin() { return screens.listIterator(0); } + public final ListIterator end() { + return screens.listIterator(screens.size()); + } public final int num_screens() { return screens.size(); } public final void add_screen(Screen screen) { screens.add(screen); } public final void remove_screen(int id) { - for (Iterator iter = screens.iterator(); iter.hasNext(); ) { - Screen refScreen = (Screen)iter.next(); - if (refScreen.id == id) + ListIterator iter, nextiter; + for (iter = begin(); iter != end(); iter = nextiter) { + nextiter = iter; nextiter.next(); + if (((Screen)iter.next()).id == id) iter.remove(); } } @@ -68,9 +73,10 @@ public class ScreenSet { } public final void debug_print() { + vlog.debug(num_screens()+" screen(s)"); for (Iterator iter = screens.iterator(); iter.hasNext(); ) { Screen refScreen = (Screen)iter.next(); - vlog.error(" "+refScreen.id+" (0x"+refScreen.id+"): "+ + vlog.debug(" "+refScreen.id+" (0x"+refScreen.id+"): "+ refScreen.dimensions.width()+"x"+refScreen.dimensions.height()+ "+"+refScreen.dimensions.tl.x+"+"+refScreen.dimensions.tl.y+ " (flags 0x"+refScreen.flags+")"); -- 2.39.5