Browse Source

Merge branch 'connparams' of https://github.com/CendioOssman/tigervnc

tags/v1.9.90
Pierre Ossman 5 years ago
parent
commit
d8bbbeb3b3
55 changed files with 1234 additions and 1084 deletions
  1. 286
    21
      common/rfb/CConnection.cxx
  2. 63
    7
      common/rfb/CConnection.h
  3. 2
    1
      common/rfb/CMakeLists.txt
  4. 18
    15
      common/rfb/CMsgHandler.cxx
  5. 8
    6
      common/rfb/CMsgHandler.h
  6. 10
    12
      common/rfb/CMsgReader.cxx
  7. 14
    91
      common/rfb/CMsgWriter.cxx
  8. 6
    5
      common/rfb/CMsgWriter.h
  9. 0
    1
      common/rfb/CSecurityTLS.cxx
  10. 0
    1
      common/rfb/CSecurityTLS.h
  11. 178
    0
      common/rfb/ClientParams.cxx
  12. 23
    32
      common/rfb/ClientParams.h
  13. 0
    200
      common/rfb/ConnParams.cxx
  14. 4
    4
      common/rfb/CopyRectDecoder.cxx
  15. 3
    3
      common/rfb/CopyRectDecoder.h
  16. 7
    7
      common/rfb/DecodeManager.cxx
  17. 1
    1
      common/rfb/DecodeManager.h
  18. 2
    2
      common/rfb/Decoder.cxx
  19. 5
    5
      common/rfb/Decoder.h
  20. 23
    19
      common/rfb/EncodeManager.cxx
  21. 5
    5
      common/rfb/HextileDecoder.cxx
  22. 2
    2
      common/rfb/HextileDecoder.h
  23. 1
    1
      common/rfb/HextileEncoder.cxx
  24. 1
    1
      common/rfb/JpegCompressor.cxx
  25. 10
    1
      common/rfb/Logger.cxx
  26. 5
    5
      common/rfb/RREDecoder.cxx
  27. 2
    2
      common/rfb/RREDecoder.h
  28. 1
    1
      common/rfb/RREEncoder.cxx
  29. 6
    6
      common/rfb/RawDecoder.cxx
  30. 2
    2
      common/rfb/RawDecoder.h
  31. 44
    26
      common/rfb/SConnection.cxx
  32. 11
    19
      common/rfb/SMsgHandler.cxx
  33. 4
    4
      common/rfb/SMsgHandler.h
  34. 105
    170
      common/rfb/SMsgWriter.cxx
  35. 13
    26
      common/rfb/SMsgWriter.h
  36. 84
    0
      common/rfb/ServerParams.cxx
  37. 89
    0
      common/rfb/ServerParams.h
  38. 12
    12
      common/rfb/TightDecoder.cxx
  39. 3
    3
      common/rfb/TightDecoder.h
  40. 1
    2
      common/rfb/TightEncoder.cxx
  41. 4
    4
      common/rfb/TightJPEGEncoder.cxx
  42. 1
    16
      common/rfb/UpdateTracker.cxx
  43. 1
    4
      common/rfb/UpdateTracker.h
  44. 53
    61
      common/rfb/VNCSConnectionST.cxx
  45. 9
    6
      common/rfb/VNCServerST.cxx
  46. 4
    4
      common/rfb/ZRLEDecoder.cxx
  47. 2
    2
      common/rfb/ZRLEDecoder.h
  48. 1
    2
      common/rfb/ZRLEEncoder.cxx
  49. 5
    5
      tests/decperf.cxx
  50. 6
    8
      tests/encperf.cxx
  51. 19
    0
      unix/x0vncserver/XDesktop.cxx
  52. 53
    212
      vncviewer/CConn.cxx
  53. 3
    20
      vncviewer/CConn.h
  54. 14
    14
      vncviewer/DesktopWindow.cxx
  55. 5
    5
      vncviewer/Viewport.cxx

+ 286
- 21
common/rfb/CConnection.cxx View File

* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA. * USA.
*/ */
#include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>


#include <rfb/CMsgReader.h> #include <rfb/CMsgReader.h>
#include <rfb/CMsgWriter.h> #include <rfb/CMsgWriter.h>
#include <rfb/CSecurity.h> #include <rfb/CSecurity.h>
#include <rfb/Decoder.h>
#include <rfb/Security.h> #include <rfb/Security.h>
#include <rfb/SecurityClient.h> #include <rfb/SecurityClient.h>
#include <rfb/CConnection.h> #include <rfb/CConnection.h>
static LogWriter vlog("CConnection"); static LogWriter vlog("CConnection");


CConnection::CConnection() CConnection::CConnection()
: csecurity(0), is(0), os(0), reader_(0), writer_(0),
: csecurity(0),
supportsLocalCursor(false), supportsDesktopResize(false),
supportsLEDState(false),
is(0), os(0), reader_(0), writer_(0),
shared(false), shared(false),
state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false), state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false),
pendingPFChange(false), preferredEncoding(encodingTight),
compressLevel(2), qualityLevel(-1),
formatChange(false), encodingChange(false),
firstUpdate(true), pendingUpdate(false), continuousUpdates(false),
forceNonincremental(true),
framebuffer(NULL), decoder(this) framebuffer(NULL), decoder(this)
{ {
} }
{ {
decoder.flush(); decoder.flush();


if (fb) {
assert(fb->width() == server.width());
assert(fb->height() == server.height());
}

if ((framebuffer != NULL) && (fb != NULL)) { if ((framebuffer != NULL) && (fb != NULL)) {
Rect rect; Rect rect;




void CConnection::processVersionMsg() void CConnection::processVersionMsg()
{ {
char verStr[13];
int majorVersion;
int minorVersion;

vlog.debug("reading protocol version"); vlog.debug("reading protocol version");
bool done;
if (!cp.readVersion(is, &done)) {

if (!is->checkNoWait(12))
return;

is->readBytes(verStr, 12);
verStr[12] = '\0';

if (sscanf(verStr, "RFB %03d.%03d\n",
&majorVersion, &minorVersion) != 2) {
state_ = RFBSTATE_INVALID; state_ = RFBSTATE_INVALID;
throw Exception("reading version failed: not an RFB server?"); throw Exception("reading version failed: not an RFB server?");
} }
if (!done) return;

server.setVersion(majorVersion, minorVersion);


vlog.info("Server supports RFB protocol version %d.%d", vlog.info("Server supports RFB protocol version %d.%d",
cp.majorVersion, cp.minorVersion);
server.majorVersion, server.minorVersion);


// The only official RFB protocol versions are currently 3.3, 3.7 and 3.8 // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
if (cp.beforeVersion(3,3)) {
if (server.beforeVersion(3,3)) {
vlog.error("Server gave unsupported RFB protocol version %d.%d", vlog.error("Server gave unsupported RFB protocol version %d.%d",
cp.majorVersion, cp.minorVersion);
server.majorVersion, server.minorVersion);
state_ = RFBSTATE_INVALID; state_ = RFBSTATE_INVALID;
throw Exception("Server gave unsupported RFB protocol version %d.%d", throw Exception("Server gave unsupported RFB protocol version %d.%d",
cp.majorVersion, cp.minorVersion);
} else if (useProtocol3_3 || cp.beforeVersion(3,7)) {
cp.setVersion(3,3);
} else if (cp.afterVersion(3,8)) {
cp.setVersion(3,8);
server.majorVersion, server.minorVersion);
} else if (useProtocol3_3 || server.beforeVersion(3,7)) {
server.setVersion(3,3);
} else if (server.afterVersion(3,8)) {
server.setVersion(3,8);
} }


cp.writeVersion(os);
sprintf(verStr, "RFB %03d.%03d\n",
server.majorVersion, server.minorVersion);
os->writeBytes(verStr, 12);
os->flush();

state_ = RFBSTATE_SECURITY_TYPES; state_ = RFBSTATE_SECURITY_TYPES;


vlog.info("Using RFB protocol version %d.%d", vlog.info("Using RFB protocol version %d.%d",
cp.majorVersion, cp.minorVersion);
server.majorVersion, server.minorVersion);
} }




std::list<rdr::U8> secTypes; std::list<rdr::U8> secTypes;
secTypes = security.GetEnabledSecTypes(); secTypes = security.GetEnabledSecTypes();


if (cp.isVersion(3,3)) {
if (server.isVersion(3,3)) {


// legacy 3.3 server may only offer "vnc authentication" or "none" // legacy 3.3 server may only offer "vnc authentication" or "none"


{ {
vlog.debug("processing security result message"); vlog.debug("processing security result message");
int result; int result;
if (cp.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
if (server.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
result = secResultOK; result = secResultOK;
} else { } else {
if (!is->checkNoWait(1)) return; if (!is->checkNoWait(1)) return;
throw Exception("Unknown security result from server"); throw Exception("Unknown security result from server");
} }
state_ = RFBSTATE_INVALID; state_ = RFBSTATE_INVALID;
if (cp.beforeVersion(3,8))
if (server.beforeVersion(3,8))
throw AuthFailureException(); throw AuthFailureException();
CharArray reason(is->readString()); CharArray reason(is->readString());
throw AuthFailureException(reason.buf); throw AuthFailureException(reason.buf);
{ {
state_ = RFBSTATE_INITIALISATION; state_ = RFBSTATE_INITIALISATION;
reader_ = new CMsgReader(this, is); reader_ = new CMsgReader(this, is);
writer_ = new CMsgWriter(&cp, os);
writer_ = new CMsgWriter(&server, os);
vlog.debug("Authentication success!"); vlog.debug("Authentication success!");
authSuccess(); authSuccess();
writer_->writeClientInit(shared); writer_->writeClientInit(shared);
decoder.flush(); decoder.flush();


CMsgHandler::setDesktopSize(w,h); CMsgHandler::setDesktopSize(w,h);

if (continuousUpdates)
writer()->writeEnableContinuousUpdates(true, 0, 0,
server.width(),
server.height());

resizeFramebuffer();
assert(framebuffer != NULL);
assert(framebuffer->width() == server.width());
assert(framebuffer->height() == server.height());
} }


void CConnection::setExtendedDesktopSize(unsigned reason, void CConnection::setExtendedDesktopSize(unsigned reason,
decoder.flush(); decoder.flush();


CMsgHandler::setExtendedDesktopSize(reason, result, w, h, layout); CMsgHandler::setExtendedDesktopSize(reason, result, w, h, layout);

if (continuousUpdates)
writer()->writeEnableContinuousUpdates(true, 0, 0,
server.width(),
server.height());

resizeFramebuffer();
assert(framebuffer != NULL);
assert(framebuffer->width() == server.width());
assert(framebuffer->height() == server.height());
}

void CConnection::endOfContinuousUpdates()
{
CMsgHandler::endOfContinuousUpdates();

// We've gotten the marker for a format change, so make the pending
// one active
if (pendingPFChange) {
server.setPF(pendingPF);
pendingPFChange = false;

// We might have another change pending
if (formatChange)
requestNewUpdate();
}
}

void CConnection::serverInit(int width, int height,
const PixelFormat& pf,
const char* name)
{
CMsgHandler::serverInit(width, height, pf, name);

state_ = RFBSTATE_NORMAL;
vlog.debug("initialisation done");

initDone();
assert(framebuffer != NULL);
assert(framebuffer->width() == server.width());
assert(framebuffer->height() == server.height());

// We want to make sure we call SetEncodings at least once
encodingChange = true;

requestNewUpdate();

// This initial update request is a bit of a corner case, so we need
// to help out setting the correct format here.
if (pendingPFChange) {
server.setPF(pendingPF);
pendingPFChange = false;
}
} }


void CConnection::readAndDecodeRect(const Rect& r, int encoding, void CConnection::readAndDecodeRect(const Rect& r, int encoding,
void CConnection::framebufferUpdateStart() void CConnection::framebufferUpdateStart()
{ {
CMsgHandler::framebufferUpdateStart(); CMsgHandler::framebufferUpdateStart();

assert(framebuffer != NULL);

// Note: This might not be true if continuous updates are supported
pendingUpdate = false;

requestNewUpdate();
} }


void CConnection::framebufferUpdateEnd() void CConnection::framebufferUpdateEnd()
decoder.flush(); decoder.flush();


CMsgHandler::framebufferUpdateEnd(); CMsgHandler::framebufferUpdateEnd();

// A format change has been scheduled and we are now past the update
// with the old format. Time to active the new one.
if (pendingPFChange && !continuousUpdates) {
server.setPF(pendingPF);
pendingPFChange = false;
}

if (firstUpdate) {
if (server.supportsContinuousUpdates) {
vlog.info("Enabling continuous updates");
continuousUpdates = true;
writer()->writeEnableContinuousUpdates(true, 0, 0,
server.width(),
server.height());
}

firstUpdate = false;
}
} }


void CConnection::dataRect(const Rect& r, int encoding) void CConnection::dataRect(const Rect& r, int encoding)
{ {
} }


void CConnection::serverInit()
void CConnection::initDone()
{ {
state_ = RFBSTATE_NORMAL;
vlog.debug("initialisation done");
}

void CConnection::resizeFramebuffer()
{
assert(false);
}

void CConnection::refreshFramebuffer()
{
forceNonincremental = true;

// Without continuous updates we have to make sure we only have a
// single update in flight, so we'll have to wait to do the refresh
if (continuousUpdates)
requestNewUpdate();
}

void CConnection::setPreferredEncoding(int encoding)
{
if (preferredEncoding == encoding)
return;

preferredEncoding = encoding;
encodingChange = true;
}

int CConnection::getPreferredEncoding()
{
return preferredEncoding;
}

void CConnection::setCompressLevel(int level)
{
if (compressLevel == level)
return;

compressLevel = level;
encodingChange = true;
}

void CConnection::setQualityLevel(int level)
{
if (qualityLevel == level)
return;

qualityLevel = level;
encodingChange = true;
}

void CConnection::setPF(const PixelFormat& pf)
{
if (server.pf().equal(pf) && !formatChange)
return;

nextPF = pf;
formatChange = true;
} }


void CConnection::fence(rdr::U32 flags, unsigned len, const char data[]) void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])


writer()->writeFence(flags, len, data); writer()->writeFence(flags, len, data);
} }

// requestNewUpdate() requests an update from the server, having set the
// format and encoding appropriately.
void CConnection::requestNewUpdate()
{
if (formatChange && !pendingPFChange) {
/* Catch incorrect requestNewUpdate calls */
assert(!pendingUpdate || continuousUpdates);

// We have to make sure we switch the internal format at a safe
// time. For continuous updates we temporarily disable updates and
// look for a EndOfContinuousUpdates message to see when to switch.
// For classical updates we just got a new update right before this
// function was called, so we need to make sure we finish that
// update before we can switch.

pendingPFChange = true;
pendingPF = nextPF;

if (continuousUpdates)
writer()->writeEnableContinuousUpdates(false, 0, 0, 0, 0);

writer()->writeSetPixelFormat(pendingPF);

if (continuousUpdates)
writer()->writeEnableContinuousUpdates(true, 0, 0,
server.width(),
server.height());

formatChange = false;
}

if (encodingChange) {
updateEncodings();
encodingChange = false;
}

if (forceNonincremental || !continuousUpdates) {
pendingUpdate = true;
writer()->writeFramebufferUpdateRequest(Rect(0, 0,
server.width(),
server.height()),
!forceNonincremental);
}

forceNonincremental = false;
}

// Ask for encodings based on which decoders are supported. Assumes higher
// encoding numbers are more desirable.

void CConnection::updateEncodings()
{
std::list<rdr::U32> encodings;

if (supportsLocalCursor) {
encodings.push_back(pseudoEncodingCursorWithAlpha);
encodings.push_back(pseudoEncodingCursor);
encodings.push_back(pseudoEncodingXCursor);
}
if (supportsDesktopResize) {
encodings.push_back(pseudoEncodingDesktopSize);
encodings.push_back(pseudoEncodingExtendedDesktopSize);
}
if (supportsLEDState)
encodings.push_back(pseudoEncodingLEDState);

encodings.push_back(pseudoEncodingDesktopName);
encodings.push_back(pseudoEncodingLastRect);
encodings.push_back(pseudoEncodingContinuousUpdates);
encodings.push_back(pseudoEncodingFence);
encodings.push_back(pseudoEncodingQEMUKeyEvent);

if (Decoder::supported(preferredEncoding)) {
encodings.push_back(preferredEncoding);
}

encodings.push_back(encodingCopyRect);

for (int i = encodingMax; i >= 0; i--) {
if ((i != preferredEncoding) && Decoder::supported(i))
encodings.push_back(i);
}

if (compressLevel >= 0 && compressLevel <= 9)
encodings.push_back(pseudoEncodingCompressLevel0 + compressLevel);
if (qualityLevel >= 0 && qualityLevel <= 9)
encodings.push_back(pseudoEncodingQualityLevel0 + qualityLevel);

writer()->writeSetEncodings(encodings);
}

+ 63
- 7
common/rfb/CConnection.h View File

int w, int h, int w, int h,
const ScreenSet& layout); const ScreenSet& layout);


virtual void endOfContinuousUpdates();

virtual void serverInit(int width, int height,
const PixelFormat& pf,
const char* name);

virtual void readAndDecodeRect(const Rect& r, int encoding, virtual void readAndDecodeRect(const Rect& r, int encoding,
ModifiablePixelBuffer* pb); ModifiablePixelBuffer* pb);




// Methods to be overridden in a derived class // Methods to be overridden in a derived class


// getIdVerifier() returns the identity verifier associated with the connection.
// Ownership of the IdentityVerifier is retained by the CConnection instance.
virtual IdentityVerifier* getIdentityVerifier() {return 0;}

// authSuccess() is called when authentication has succeeded. // authSuccess() is called when authentication has succeeded.
virtual void authSuccess(); virtual void authSuccess();


// serverInit() is called when the ServerInit message is received. The
// derived class must call on to CConnection::serverInit().
virtual void serverInit();
// initDone() is called when the connection is fully established
// and standard messages can be sent. This is called before the
// initial FramebufferUpdateRequest giving a derived class the
// chance to modify pixel format and settings. The derived class
// must also make sure it has provided a valid framebuffer before
// returning.
virtual void initDone() = 0;

// resizeFramebuffer() is called whenever the framebuffer
// dimensions or the screen layout changes. A subclass must make
// sure the pixel buffer has been updated once this call returns.
virtual void resizeFramebuffer();




// Other methods // Other methods


// refreshFramebuffer() forces a complete refresh of the entire
// framebuffer
void refreshFramebuffer();

// setPreferredEncoding()/getPreferredEncoding() adjusts which
// encoding is listed first as a hint to the server that it is the
// preferred one
void setPreferredEncoding(int encoding);
int getPreferredEncoding();
// setCompressLevel()/setQualityLevel() controls the encoding hints
// sent to the server
void setCompressLevel(int level);
void setQualityLevel(int level);
// setPF() controls the pixel format requested from the server.
// server.pf() will automatically be adjusted once the new format
// is active.
void setPF(const PixelFormat& pf);

CMsgReader* reader() { return reader_; } CMsgReader* reader() { return reader_; }
CMsgWriter* writer() { return writer_; } CMsgWriter* writer() { return writer_; }




ModifiablePixelBuffer* getFramebuffer() { return framebuffer; } ModifiablePixelBuffer* getFramebuffer() { return framebuffer; }


protected:
// Optional capabilities that a subclass is expected to set to true
// if supported
bool supportsLocalCursor;
bool supportsDesktopResize;
bool supportsLEDState;

private: private:
// This is a default implementation of fences that automatically // This is a default implementation of fences that automatically
// responds to requests, stating no support for synchronisation. // responds to requests, stating no support for synchronisation.
void throwConnFailedException(); void throwConnFailedException();
void securityCompleted(); void securityCompleted();


void requestNewUpdate();
void updateEncodings();

rdr::InStream* is; rdr::InStream* is;
rdr::OutStream* os; rdr::OutStream* os;
CMsgReader* reader_; CMsgReader* reader_;


bool useProtocol3_3; bool useProtocol3_3;


bool pendingPFChange;
rfb::PixelFormat pendingPF;

int preferredEncoding;
int compressLevel;
int qualityLevel;

bool formatChange;
rfb::PixelFormat nextPF;
bool encodingChange;

bool firstUpdate;
bool pendingUpdate;
bool continuousUpdates;

bool forceNonincremental;

ModifiablePixelBuffer* framebuffer; ModifiablePixelBuffer* framebuffer;
DecodeManager decoder; DecodeManager decoder;
}; };

+ 2
- 1
common/rfb/CMakeLists.txt View File

CSecurityStack.cxx CSecurityStack.cxx
CSecurityVeNCrypt.cxx CSecurityVeNCrypt.cxx
CSecurityVncAuth.cxx CSecurityVncAuth.cxx
ClientParams.cxx
ComparingUpdateTracker.cxx ComparingUpdateTracker.cxx
Configuration.cxx Configuration.cxx
ConnParams.cxx
CopyRectDecoder.cxx CopyRectDecoder.cxx
Cursor.cxx Cursor.cxx
DecodeManager.cxx DecodeManager.cxx
SMsgReader.cxx SMsgReader.cxx
SMsgWriter.cxx SMsgWriter.cxx
ServerCore.cxx ServerCore.cxx
ServerParams.cxx
Security.cxx Security.cxx
SecurityServer.cxx SecurityServer.cxx
SecurityClient.cxx SecurityClient.cxx

+ 18
- 15
common/rfb/CMsgHandler.cxx View File



void CMsgHandler::setDesktopSize(int width, int height) void CMsgHandler::setDesktopSize(int width, int height)
{ {
cp.width = width;
cp.height = height;
server.setDimensions(width, height);
} }


void CMsgHandler::setExtendedDesktopSize(unsigned reason, unsigned result, void CMsgHandler::setExtendedDesktopSize(unsigned reason, unsigned result,
int width, int height, int width, int height,
const ScreenSet& layout) const ScreenSet& layout)
{ {
cp.supportsSetDesktopSize = true;
server.supportsSetDesktopSize = true;


if ((reason == reasonClient) && (result != resultSuccess)) if ((reason == reasonClient) && (result != resultSuccess))
return; return;


if (!layout.validate(width, height))
fprintf(stderr, "Server sent us an invalid screen layout\n");

cp.width = width;
cp.height = height;
cp.screenLayout = layout;
server.setDimensions(width, height, layout);
} }


void CMsgHandler::setPixelFormat(const PixelFormat& pf) void CMsgHandler::setPixelFormat(const PixelFormat& pf)
{ {
cp.setPF(pf);
server.setPF(pf);
} }


void CMsgHandler::setName(const char* name) void CMsgHandler::setName(const char* name)
{ {
cp.setName(name);
server.setName(name);
} }


void CMsgHandler::fence(rdr::U32 flags, unsigned len, const char data[]) void CMsgHandler::fence(rdr::U32 flags, unsigned len, const char data[])
{ {
cp.supportsFence = true;
server.supportsFence = true;
} }


void CMsgHandler::endOfContinuousUpdates() void CMsgHandler::endOfContinuousUpdates()
{ {
cp.supportsContinuousUpdates = true;
server.supportsContinuousUpdates = true;
} }


void CMsgHandler::supportsQEMUKeyEvent() void CMsgHandler::supportsQEMUKeyEvent()
{ {
cp.supportsQEMUKeyEvent = true;
server.supportsQEMUKeyEvent = true;
}

void CMsgHandler::serverInit(int width, int height,
const PixelFormat& pf,
const char* name)
{
server.setDimensions(width, height);
server.setPF(pf);
server.setName(name);
} }


void CMsgHandler::framebufferUpdateStart() void CMsgHandler::framebufferUpdateStart()


void CMsgHandler::setLEDState(unsigned int state) void CMsgHandler::setLEDState(unsigned int state)
{ {
cp.setLEDState(state);
server.setLEDState(state);
} }

+ 8
- 6
common/rfb/CMsgHandler.h View File



#include <rdr/types.h> #include <rdr/types.h>
#include <rfb/Pixel.h> #include <rfb/Pixel.h>
#include <rfb/ConnParams.h>
#include <rfb/ServerParams.h>
#include <rfb/Rect.h> #include <rfb/Rect.h>
#include <rfb/ScreenSet.h> #include <rfb/ScreenSet.h>




// The following methods are called as corresponding messages are read. A // The following methods are called as corresponding messages are read. A
// derived class should override these methods as desired. Note that for // derived class should override these methods as desired. Note that for
// the setDesktopSize(), setExtendedDesktopSize(), setPixelFormat() and
// setName() methods, a derived class should call on to CMsgHandler's
// methods to set the members of cp appropriately.
// the setDesktopSize(), setExtendedDesktopSize(), setPixelFormat(),
// setName() and serverInit() methods, a derived class should call on to
// CMsgHandler's methods to set the members of "server" appropriately.


virtual void setDesktopSize(int w, int h); virtual void setDesktopSize(int w, int h);
virtual void setExtendedDesktopSize(unsigned reason, unsigned result, virtual void setExtendedDesktopSize(unsigned reason, unsigned result,
virtual void fence(rdr::U32 flags, unsigned len, const char data[]); virtual void fence(rdr::U32 flags, unsigned len, const char data[]);
virtual void endOfContinuousUpdates(); virtual void endOfContinuousUpdates();
virtual void supportsQEMUKeyEvent(); virtual void supportsQEMUKeyEvent();
virtual void serverInit() = 0;
virtual void serverInit(int width, int height,
const PixelFormat& pf,
const char* name) = 0;


virtual void readAndDecodeRect(const Rect& r, int encoding, virtual void readAndDecodeRect(const Rect& r, int encoding,
ModifiablePixelBuffer* pb) = 0; ModifiablePixelBuffer* pb) = 0;


virtual void setLEDState(unsigned int state); virtual void setLEDState(unsigned int state);


ConnParams cp;
ServerParams server;
}; };
} }
#endif #endif

+ 10
- 12
common/rfb/CMsgReader.cxx View File

{ {
int width = is->readU16(); int width = is->readU16();
int height = is->readU16(); int height = is->readU16();
handler->setDesktopSize(width, height);
PixelFormat pf; PixelFormat pf;
pf.read(is); pf.read(is);
handler->setPixelFormat(pf);
CharArray name(is->readString()); CharArray name(is->readString());
handler->setName(name.buf);
handler->serverInit();
handler->serverInit(width, height, pf, name.buf);
} }


void CMsgReader::readMsg() void CMsgReader::readMsg()


void CMsgReader::readRect(const Rect& r, int encoding) void CMsgReader::readRect(const Rect& r, int encoding)
{ {
if ((r.br.x > handler->cp.width) || (r.br.y > handler->cp.height)) {
if ((r.br.x > handler->server.width()) ||
(r.br.y > handler->server.height())) {
fprintf(stderr, "Rect too big: %dx%d at %d,%d exceeds %dx%d\n", fprintf(stderr, "Rect too big: %dx%d at %d,%d exceeds %dx%d\n",
r.width(), r.height(), r.tl.x, r.tl.y, r.width(), r.height(), r.tl.x, r.tl.y,
handler->cp.width, handler->cp.height);
handler->server.width(), handler->server.height());
throw Exception("Rect too big"); throw Exception("Rect too big");
} }


if (width > maxCursorSize || height > maxCursorSize) if (width > maxCursorSize || height > maxCursorSize)
throw Exception("Too big cursor"); throw Exception("Too big cursor");


int data_len = width * height * (handler->cp.pf().bpp/8);
int data_len = width * height * (handler->server.pf().bpp/8);
int mask_len = ((width+7)/8) * height; int mask_len = ((width+7)/8) * height;
rdr::U8Array data(data_len); rdr::U8Array data(data_len);
rdr::U8Array mask(mask_len); rdr::U8Array mask(mask_len);
int byte = y * maskBytesPerRow + x / 8; int byte = y * maskBytesPerRow + x / 8;
int bit = 7 - x % 8; int bit = 7 - x % 8;


handler->cp.pf().rgbFromBuffer(out, in, 1);
handler->server.pf().rgbFromBuffer(out, in, 1);


if (mask.buf[byte] & (1 << bit)) if (mask.buf[byte] & (1 << bit))
out[3] = 255; out[3] = 255;
else else
out[3] = 0; out[3] = 0;


in += handler->cp.pf().bpp/8;
in += handler->server.pf().bpp/8;
out += 4; out += 4;
} }
} }


encoding = is->readS32(); encoding = is->readS32();


origPF = handler->cp.pf();
handler->cp.setPF(rgbaPF);
origPF = handler->server.pf();
handler->server.setPF(rgbaPF);
handler->readAndDecodeRect(pb.getRect(), encoding, &pb); handler->readAndDecodeRect(pb.getRect(), encoding, &pb);
handler->cp.setPF(origPF);
handler->server.setPF(origPF);


// On-wire data has pre-multiplied alpha, but we store it // On-wire data has pre-multiplied alpha, but we store it
// non-pre-multiplied // non-pre-multiplied

+ 14
- 91
common/rfb/CMsgWriter.cxx View File

#include <rdr/OutStream.h> #include <rdr/OutStream.h>
#include <rfb/msgTypes.h> #include <rfb/msgTypes.h>
#include <rfb/fenceTypes.h> #include <rfb/fenceTypes.h>
#include <rfb/encodings.h>
#include <rfb/qemuTypes.h> #include <rfb/qemuTypes.h>
#include <rfb/Exception.h> #include <rfb/Exception.h>
#include <rfb/PixelFormat.h> #include <rfb/PixelFormat.h>
#include <rfb/Rect.h> #include <rfb/Rect.h>
#include <rfb/ConnParams.h>
#include <rfb/Decoder.h>
#include <rfb/ServerParams.h>
#include <rfb/CMsgWriter.h> #include <rfb/CMsgWriter.h>


using namespace rfb; using namespace rfb;


CMsgWriter::CMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
: cp(cp_), os(os_)
CMsgWriter::CMsgWriter(ServerParams* server_, rdr::OutStream* os_)
: server(server_), os(os_)
{ {
} }


endMsg(); endMsg();
} }


void CMsgWriter::writeSetEncodings(int nEncodings, rdr::U32* encodings)
void CMsgWriter::writeSetEncodings(const std::list<rdr::U32> encodings)
{ {
std::list<rdr::U32>::const_iterator iter;
startMsg(msgTypeSetEncodings); startMsg(msgTypeSetEncodings);
os->skip(1); os->skip(1);
os->writeU16(nEncodings);
for (int i = 0; i < nEncodings; i++)
os->writeU32(encodings[i]);
os->writeU16(encodings.size());
for (iter = encodings.begin(); iter != encodings.end(); ++iter)
os->writeU32(*iter);
endMsg(); endMsg();
} }


// Ask for encodings based on which decoders are supported. Assumes higher
// encoding numbers are more desirable.

void CMsgWriter::writeSetEncodings(int preferredEncoding, bool useCopyRect)
{
int nEncodings = 0;
rdr::U32 encodings[encodingMax+3];

if (cp->supportsLocalCursor) {
encodings[nEncodings++] = pseudoEncodingCursorWithAlpha;
encodings[nEncodings++] = pseudoEncodingCursor;
encodings[nEncodings++] = pseudoEncodingXCursor;
}
if (cp->supportsDesktopResize)
encodings[nEncodings++] = pseudoEncodingDesktopSize;
if (cp->supportsExtendedDesktopSize)
encodings[nEncodings++] = pseudoEncodingExtendedDesktopSize;
if (cp->supportsDesktopRename)
encodings[nEncodings++] = pseudoEncodingDesktopName;
if (cp->supportsLEDState)
encodings[nEncodings++] = pseudoEncodingLEDState;

encodings[nEncodings++] = pseudoEncodingLastRect;
encodings[nEncodings++] = pseudoEncodingContinuousUpdates;
encodings[nEncodings++] = pseudoEncodingFence;
encodings[nEncodings++] = pseudoEncodingQEMUKeyEvent;

if (Decoder::supported(preferredEncoding)) {
encodings[nEncodings++] = preferredEncoding;
}

if (useCopyRect) {
encodings[nEncodings++] = encodingCopyRect;
}

/*
* Prefer encodings in this order:
*
* Tight, ZRLE, Hextile, *
*/

if ((preferredEncoding != encodingTight) &&
Decoder::supported(encodingTight))
encodings[nEncodings++] = encodingTight;

if ((preferredEncoding != encodingZRLE) &&
Decoder::supported(encodingZRLE))
encodings[nEncodings++] = encodingZRLE;

if ((preferredEncoding != encodingHextile) &&
Decoder::supported(encodingHextile))
encodings[nEncodings++] = encodingHextile;

// Remaining encodings
for (int i = encodingMax; i >= 0; i--) {
switch (i) {
case encodingCopyRect:
case encodingTight:
case encodingZRLE:
case encodingHextile:
/* These have already been sent earlier */
break;
default:
if ((i != preferredEncoding) && Decoder::supported(i))
encodings[nEncodings++] = i;
}
}

if (cp->compressLevel >= 0 && cp->compressLevel <= 9)
encodings[nEncodings++] = pseudoEncodingCompressLevel0 + cp->compressLevel;
if (cp->qualityLevel >= 0 && cp->qualityLevel <= 9)
encodings[nEncodings++] = pseudoEncodingQualityLevel0 + cp->qualityLevel;

writeSetEncodings(nEncodings, encodings);
}

void CMsgWriter::writeSetDesktopSize(int width, int height, void CMsgWriter::writeSetDesktopSize(int width, int height,
const ScreenSet& layout) const ScreenSet& layout)
{ {
if (!cp->supportsSetDesktopSize)
if (!server->supportsSetDesktopSize)
throw Exception("Server does not support SetDesktopSize"); throw Exception("Server does not support SetDesktopSize");


startMsg(msgTypeSetDesktopSize); startMsg(msgTypeSetDesktopSize);
void CMsgWriter::writeEnableContinuousUpdates(bool enable, void CMsgWriter::writeEnableContinuousUpdates(bool enable,
int x, int y, int w, int h) int x, int y, int w, int h)
{ {
if (!cp->supportsContinuousUpdates)
if (!server->supportsContinuousUpdates)
throw Exception("Server does not support continuous updates"); throw Exception("Server does not support continuous updates");


startMsg(msgTypeEnableContinuousUpdates); startMsg(msgTypeEnableContinuousUpdates);


void CMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[]) void CMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
{ {
if (!cp->supportsFence)
if (!server->supportsFence)
throw Exception("Server does not support fences"); throw Exception("Server does not support fences");
if (len > 64) if (len > 64)
throw Exception("Too large fence payload"); throw Exception("Too large fence payload");


void CMsgWriter::writeKeyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) void CMsgWriter::writeKeyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
{ {
if (!cp->supportsQEMUKeyEvent || !keycode) {
if (!server->supportsQEMUKeyEvent || !keycode) {
/* This event isn't meaningful without a valid keysym */ /* This event isn't meaningful without a valid keysym */
if (!keysym) if (!keysym)
return; return;
Point p(pos); Point p(pos);
if (p.x < 0) p.x = 0; if (p.x < 0) p.x = 0;
if (p.y < 0) p.y = 0; if (p.y < 0) p.y = 0;
if (p.x >= cp->width) p.x = cp->width - 1;
if (p.y >= cp->height) p.y = cp->height - 1;
if (p.x >= server->width()) p.x = server->width() - 1;
if (p.y >= server->height()) p.y = server->height() - 1;


startMsg(msgTypePointerEvent); startMsg(msgTypePointerEvent);
os->writeU8(buttonMask); os->writeU8(buttonMask);

+ 6
- 5
common/rfb/CMsgWriter.h View File

#ifndef __RFB_CMSGWRITER_H__ #ifndef __RFB_CMSGWRITER_H__
#define __RFB_CMSGWRITER_H__ #define __RFB_CMSGWRITER_H__


#include <list>

#include <rdr/types.h> #include <rdr/types.h>


namespace rdr { class OutStream; } namespace rdr { class OutStream; }
namespace rfb { namespace rfb {


class PixelFormat; class PixelFormat;
class ConnParams;
class ServerParams;
struct ScreenSet; struct ScreenSet;
struct Point; struct Point;
struct Rect; struct Rect;


class CMsgWriter { class CMsgWriter {
public: public:
CMsgWriter(ConnParams* cp, rdr::OutStream* os);
CMsgWriter(ServerParams* server, rdr::OutStream* os);
virtual ~CMsgWriter(); virtual ~CMsgWriter();


void writeClientInit(bool shared); void writeClientInit(bool shared);


void writeSetPixelFormat(const PixelFormat& pf); void writeSetPixelFormat(const PixelFormat& pf);
void writeSetEncodings(int nEncodings, rdr::U32* encodings);
void writeSetEncodings(int preferredEncoding, bool useCopyRect);
void writeSetEncodings(const std::list<rdr::U32> encodings);
void writeSetDesktopSize(int width, int height, const ScreenSet& layout); void writeSetDesktopSize(int width, int height, const ScreenSet& layout);


void writeFramebufferUpdateRequest(const Rect& r,bool incremental); void writeFramebufferUpdateRequest(const Rect& r,bool incremental);
void startMsg(int type); void startMsg(int type);
void endMsg(); void endMsg();


ConnParams* cp;
ServerParams* server;
rdr::OutStream* os; rdr::OutStream* os;
}; };
} }

+ 0
- 1
common/rfb/CSecurityTLS.cxx View File

#endif #endif


#include <rfb/CSecurityTLS.h> #include <rfb/CSecurityTLS.h>
#include <rfb/SSecurityVeNCrypt.h>
#include <rfb/CConnection.h> #include <rfb/CConnection.h>
#include <rfb/LogWriter.h> #include <rfb/LogWriter.h>
#include <rfb/Exception.h> #include <rfb/Exception.h>

+ 0
- 1
common/rfb/CSecurityTLS.h View File

#endif #endif


#include <rfb/CSecurity.h> #include <rfb/CSecurity.h>
#include <rfb/SSecurityVeNCrypt.h>
#include <rfb/Security.h> #include <rfb/Security.h>
#include <rfb/UserMsgBox.h> #include <rfb/UserMsgBox.h>
#include <rdr/InStream.h> #include <rdr/InStream.h>

+ 178
- 0
common/rfb/ClientParams.cxx View File

/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
* Copyright 2014-2018 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include <rfb/Exception.h>
#include <rfb/encodings.h>
#include <rfb/ledStates.h>
#include <rfb/ClientParams.h>

using namespace rfb;

ClientParams::ClientParams()
: majorVersion(0), minorVersion(0),
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
subsampling(subsampleUndefined),
width_(0), height_(0), name_(0),
ledState_(ledUnknown)
{
setName("");
cursor_ = new Cursor(0, 0, Point(), NULL);
}

ClientParams::~ClientParams()
{
delete [] name_;
delete cursor_;
}

void ClientParams::setDimensions(int width, int height)
{
ScreenSet layout;
layout.add_screen(rfb::Screen(0, 0, 0, width, height, 0));
setDimensions(width, height, layout);
}

void ClientParams::setDimensions(int width, int height, const ScreenSet& layout)
{
if (!layout.validate(width, height))
throw Exception("Attempted to configure an invalid screen layout");

width_ = width;
height_ = height;
screenLayout_ = layout;
}

void ClientParams::setPF(const PixelFormat& pf)
{
pf_ = pf;

if (pf.bpp != 8 && pf.bpp != 16 && pf.bpp != 32)
throw Exception("setPF: not 8, 16 or 32 bpp?");
}

void ClientParams::setName(const char* name)
{
delete [] name_;
name_ = strDup(name);
}

void ClientParams::setCursor(const Cursor& other)
{
delete cursor_;
cursor_ = new Cursor(other);
}

bool ClientParams::supportsEncoding(rdr::S32 encoding) const
{
return encodings_.count(encoding) != 0;
}

void ClientParams::setEncodings(int nEncodings, const rdr::S32* encodings)
{
compressLevel = -1;
qualityLevel = -1;
fineQualityLevel = -1;
subsampling = subsampleUndefined;

encodings_.clear();
encodings_.insert(encodingRaw);

for (int i = nEncodings-1; i >= 0; i--) {
switch (encodings[i]) {
case pseudoEncodingSubsamp1X:
subsampling = subsampleNone;
break;
case pseudoEncodingSubsampGray:
subsampling = subsampleGray;
break;
case pseudoEncodingSubsamp2X:
subsampling = subsample2X;
break;
case pseudoEncodingSubsamp4X:
subsampling = subsample4X;
break;
case pseudoEncodingSubsamp8X:
subsampling = subsample8X;
break;
case pseudoEncodingSubsamp16X:
subsampling = subsample16X;
break;
}

if (encodings[i] >= pseudoEncodingCompressLevel0 &&
encodings[i] <= pseudoEncodingCompressLevel9)
compressLevel = encodings[i] - pseudoEncodingCompressLevel0;

if (encodings[i] >= pseudoEncodingQualityLevel0 &&
encodings[i] <= pseudoEncodingQualityLevel9)
qualityLevel = encodings[i] - pseudoEncodingQualityLevel0;

if (encodings[i] >= pseudoEncodingFineQualityLevel0 &&
encodings[i] <= pseudoEncodingFineQualityLevel100)
fineQualityLevel = encodings[i] - pseudoEncodingFineQualityLevel0;

encodings_.insert(encodings[i]);
}
}

void ClientParams::setLEDState(unsigned int state)
{
ledState_ = state;
}

bool ClientParams::supportsLocalCursor() const
{
if (supportsEncoding(pseudoEncodingCursorWithAlpha))
return true;
if (supportsEncoding(pseudoEncodingCursor))
return true;
if (supportsEncoding(pseudoEncodingXCursor))
return true;
return false;
}

bool ClientParams::supportsDesktopSize() const
{
if (supportsEncoding(pseudoEncodingExtendedDesktopSize))
return true;
if (supportsEncoding(pseudoEncodingDesktopSize))
return true;
return false;
}

bool ClientParams::supportsLEDState() const
{
if (supportsEncoding(pseudoEncodingLEDState))
return true;
return false;
}

bool ClientParams::supportsFence() const
{
if (supportsEncoding(pseudoEncodingFence))
return true;
return false;
}

bool ClientParams::supportsContinuousUpdates() const
{
if (supportsEncoding(pseudoEncodingContinuousUpdates))
return true;
return false;
}

common/rfb/ConnParams.h → common/rfb/ClientParams.h View File

/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2014 Pierre Ossman for Cendio AB
* Copyright 2014-2018 Pierre Ossman for Cendio AB
* *
* This is free software; you can redistribute it and/or modify * This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* USA. * USA.
*/ */
// //
// ConnParams - structure containing the connection parameters.
// ClientParams - structure describing the current state of the remote client
// //


#ifndef __RFB_CONNPARAMS_H__
#define __RFB_CONNPARAMS_H__
#ifndef __RFB_CLIENTPARAMS_H__
#define __RFB_CLIENTPARAMS_H__


#include <set> #include <set>


#include <rfb/PixelFormat.h> #include <rfb/PixelFormat.h>
#include <rfb/ScreenSet.h> #include <rfb/ScreenSet.h>


namespace rdr { class InStream; }

namespace rfb { namespace rfb {


const int subsampleUndefined = -1; const int subsampleUndefined = -1;
const int subsample8X = 4; const int subsample8X = 4;
const int subsample16X = 5; const int subsample16X = 5;


class ConnParams {
class ClientParams {
public: public:
ConnParams();
~ConnParams();

bool readVersion(rdr::InStream* is, bool* done);
void writeVersion(rdr::OutStream* os);
ClientParams();
~ClientParams();


int majorVersion; int majorVersion;
int minorVersion; int minorVersion;
return !beforeVersion(major,minor+1); return !beforeVersion(major,minor+1);
} }


int width;
int height;
ScreenSet screenLayout;
const int width() const { return width_; }
const int height() const { return height_; }
const ScreenSet& screenLayout() const { return screenLayout_; }
void setDimensions(int width, int height);
void setDimensions(int width, int height, const ScreenSet& layout);


const PixelFormat& pf() const { return pf_; } const PixelFormat& pf() const { return pf_; }
void setPF(const PixelFormat& pf); void setPF(const PixelFormat& pf);
unsigned int ledState() { return ledState_; } unsigned int ledState() { return ledState_; }
void setLEDState(unsigned int state); void setLEDState(unsigned int state);


bool useCopyRect;

bool supportsLocalCursor;
bool supportsLocalXCursor;
bool supportsLocalCursorWithAlpha;
bool supportsDesktopResize;
bool supportsExtendedDesktopSize;
bool supportsDesktopRename;
bool supportsLastRect;
bool supportsLEDState;
bool supportsQEMUKeyEvent;

bool supportsSetDesktopSize;
bool supportsFence;
bool supportsContinuousUpdates;
// Wrappers to check for functionality rather than specific
// encodings
bool supportsLocalCursor() const;
bool supportsDesktopSize() const;
bool supportsLEDState() const;
bool supportsFence() const;
bool supportsContinuousUpdates() const;


int compressLevel; int compressLevel;
int qualityLevel; int qualityLevel;


private: private:


int width_;
int height_;
ScreenSet screenLayout_;

PixelFormat pf_; PixelFormat pf_;
char* name_; char* name_;
Cursor* cursor_; Cursor* cursor_;
std::set<rdr::S32> encodings_; std::set<rdr::S32> encodings_;
char verStr[13];
int verStrPos;
unsigned int ledState_; unsigned int ledState_;
}; };
} }

+ 0
- 200
common/rfb/ConnParams.cxx View File

/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
* Copyright 2014 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include <stdio.h>
#include <rdr/InStream.h>
#include <rdr/OutStream.h>
#include <rfb/Exception.h>
#include <rfb/encodings.h>
#include <rfb/ledStates.h>
#include <rfb/ConnParams.h>
#include <rfb/util.h>

using namespace rfb;

ConnParams::ConnParams()
: majorVersion(0), minorVersion(0),
width(0), height(0), useCopyRect(false),
supportsLocalCursor(false), supportsLocalXCursor(false),
supportsLocalCursorWithAlpha(false),
supportsDesktopResize(false), supportsExtendedDesktopSize(false),
supportsDesktopRename(false), supportsLastRect(false),
supportsLEDState(false), supportsQEMUKeyEvent(false),
supportsSetDesktopSize(false), supportsFence(false),
supportsContinuousUpdates(false),
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
subsampling(subsampleUndefined), name_(0), verStrPos(0),
ledState_(ledUnknown)
{
setName("");
cursor_ = new Cursor(0, 0, Point(), NULL);
}

ConnParams::~ConnParams()
{
delete [] name_;
delete cursor_;
}

bool ConnParams::readVersion(rdr::InStream* is, bool* done)
{
if (verStrPos >= 12) return false;
while (is->checkNoWait(1) && verStrPos < 12) {
verStr[verStrPos++] = is->readU8();
}

if (verStrPos < 12) {
*done = false;
return true;
}
*done = true;
verStr[12] = 0;
return (sscanf(verStr, "RFB %03d.%03d\n", &majorVersion,&minorVersion) == 2);
}

void ConnParams::writeVersion(rdr::OutStream* os)
{
char str[13];
sprintf(str, "RFB %03d.%03d\n", majorVersion, minorVersion);
os->writeBytes(str, 12);
os->flush();
}

void ConnParams::setPF(const PixelFormat& pf)
{
pf_ = pf;

if (pf.bpp != 8 && pf.bpp != 16 && pf.bpp != 32)
throw Exception("setPF: not 8, 16 or 32 bpp?");
}

void ConnParams::setName(const char* name)
{
delete [] name_;
name_ = strDup(name);
}

void ConnParams::setCursor(const Cursor& other)
{
delete cursor_;
cursor_ = new Cursor(other);
}

bool ConnParams::supportsEncoding(rdr::S32 encoding) const
{
return encodings_.count(encoding) != 0;
}

void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
{
useCopyRect = false;
supportsLocalCursor = false;
supportsLocalCursorWithAlpha = false;
supportsDesktopResize = false;
supportsExtendedDesktopSize = false;
supportsLocalXCursor = false;
supportsLastRect = false;
supportsQEMUKeyEvent = false;
compressLevel = -1;
qualityLevel = -1;
fineQualityLevel = -1;
subsampling = subsampleUndefined;

encodings_.clear();
encodings_.insert(encodingRaw);

for (int i = nEncodings-1; i >= 0; i--) {
switch (encodings[i]) {
case encodingCopyRect:
useCopyRect = true;
break;
case pseudoEncodingCursor:
supportsLocalCursor = true;
break;
case pseudoEncodingXCursor:
supportsLocalXCursor = true;
break;
case pseudoEncodingCursorWithAlpha:
supportsLocalCursorWithAlpha = true;
break;
case pseudoEncodingDesktopSize:
supportsDesktopResize = true;
break;
case pseudoEncodingExtendedDesktopSize:
supportsExtendedDesktopSize = true;
break;
case pseudoEncodingDesktopName:
supportsDesktopRename = true;
break;
case pseudoEncodingLastRect:
supportsLastRect = true;
break;
case pseudoEncodingLEDState:
supportsLEDState = true;
break;
case pseudoEncodingQEMUKeyEvent:
supportsQEMUKeyEvent = true;
break;
case pseudoEncodingFence:
supportsFence = true;
break;
case pseudoEncodingContinuousUpdates:
supportsContinuousUpdates = true;
break;
case pseudoEncodingSubsamp1X:
subsampling = subsampleNone;
break;
case pseudoEncodingSubsampGray:
subsampling = subsampleGray;
break;
case pseudoEncodingSubsamp2X:
subsampling = subsample2X;
break;
case pseudoEncodingSubsamp4X:
subsampling = subsample4X;
break;
case pseudoEncodingSubsamp8X:
subsampling = subsample8X;
break;
case pseudoEncodingSubsamp16X:
subsampling = subsample16X;
break;
}

if (encodings[i] >= pseudoEncodingCompressLevel0 &&
encodings[i] <= pseudoEncodingCompressLevel9)
compressLevel = encodings[i] - pseudoEncodingCompressLevel0;

if (encodings[i] >= pseudoEncodingQualityLevel0 &&
encodings[i] <= pseudoEncodingQualityLevel9)
qualityLevel = encodings[i] - pseudoEncodingQualityLevel0;

if (encodings[i] >= pseudoEncodingFineQualityLevel0 &&
encodings[i] <= pseudoEncodingFineQualityLevel100)
fineQualityLevel = encodings[i] - pseudoEncodingFineQualityLevel0;

if (encodings[i] > 0)
encodings_.insert(encodings[i]);
}
}

void ConnParams::setLEDState(unsigned int state)
{
ledState_ = state;
}

+ 4
- 4
common/rfb/CopyRectDecoder.cxx View File

} }


void CopyRectDecoder::readRect(const Rect& r, rdr::InStream* is, void CopyRectDecoder::readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os)
const ServerParams& server, rdr::OutStream* os)
{ {
os->copyBytes(is, 4); os->copyBytes(is, 4);
} }
void CopyRectDecoder::getAffectedRegion(const Rect& rect, void CopyRectDecoder::getAffectedRegion(const Rect& rect,
const void* buffer, const void* buffer,
size_t buflen, size_t buflen,
const ConnParams& cp,
const ServerParams& server,
Region* region) Region* region)
{ {
rdr::MemInStream is(buffer, buflen); rdr::MemInStream is(buffer, buflen);
int srcX = is.readU16(); int srcX = is.readU16();
int srcY = is.readU16(); int srcY = is.readU16();


Decoder::getAffectedRegion(rect, buffer, buflen, cp, region);
Decoder::getAffectedRegion(rect, buffer, buflen, server, region);


region->assign_union(Region(rect.translate(Point(srcX-rect.tl.x, region->assign_union(Region(rect.translate(Point(srcX-rect.tl.x,
srcY-rect.tl.y)))); srcY-rect.tl.y))));
} }


void CopyRectDecoder::decodeRect(const Rect& r, const void* buffer, void CopyRectDecoder::decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
size_t buflen, const ServerParams& server,
ModifiablePixelBuffer* pb) ModifiablePixelBuffer* pb)
{ {
rdr::MemInStream is(buffer, buflen); rdr::MemInStream is(buffer, buflen);

+ 3
- 3
common/rfb/CopyRectDecoder.h View File

CopyRectDecoder(); CopyRectDecoder();
virtual ~CopyRectDecoder(); virtual ~CopyRectDecoder();
virtual void readRect(const Rect& r, rdr::InStream* is, virtual void readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os);
const ServerParams& server, rdr::OutStream* os);
virtual void getAffectedRegion(const Rect& rect, const void* buffer, virtual void getAffectedRegion(const Rect& rect, const void* buffer,
size_t buflen, const ConnParams& cp,
size_t buflen, const ServerParams& server,
Region* region); Region* region);
virtual void decodeRect(const Rect& r, const void* buffer, virtual void decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
size_t buflen, const ServerParams& server,
ModifiablePixelBuffer* pb); ModifiablePixelBuffer* pb);
}; };
} }

+ 7
- 7
common/rfb/DecodeManager.cxx View File

if (threads.empty()) { if (threads.empty()) {
bufferStream = freeBuffers.front(); bufferStream = freeBuffers.front();
bufferStream->clear(); bufferStream->clear();
decoder->readRect(r, conn->getInStream(), conn->cp, bufferStream);
decoder->readRect(r, conn->getInStream(), conn->server, bufferStream);
decoder->decodeRect(r, bufferStream->data(), bufferStream->length(), decoder->decodeRect(r, bufferStream->data(), bufferStream->length(),
conn->cp, pb);
conn->server, pb);
return; return;
} }




// Read the rect // Read the rect
bufferStream->clear(); bufferStream->clear();
decoder->readRect(r, conn->getInStream(), conn->cp, bufferStream);
decoder->readRect(r, conn->getInStream(), conn->server, bufferStream);


// Then try to put it on the queue // Then try to put it on the queue
entry = new QueueEntry; entry = new QueueEntry;
entry->rect = r; entry->rect = r;
entry->encoding = encoding; entry->encoding = encoding;
entry->decoder = decoder; entry->decoder = decoder;
entry->cp = &conn->cp;
entry->server = &conn->server;
entry->pb = pb; entry->pb = pb;
entry->bufferStream = bufferStream; entry->bufferStream = bufferStream;


decoder->getAffectedRegion(r, bufferStream->data(), decoder->getAffectedRegion(r, bufferStream->data(),
bufferStream->length(), conn->cp,
bufferStream->length(), conn->server,
&entry->affectedRegion); &entry->affectedRegion);


queueMutex->lock(); queueMutex->lock();
try { try {
entry->decoder->decodeRect(entry->rect, entry->bufferStream->data(), entry->decoder->decodeRect(entry->rect, entry->bufferStream->data(),
entry->bufferStream->length(), entry->bufferStream->length(),
*entry->cp, entry->pb);
*entry->server, entry->pb);
} catch (rdr::Exception& e) { } catch (rdr::Exception& e) {
manager->setThreadException(e); manager->setThreadException(e);
} catch(...) { } catch(...) {
(*iter2)->rect, (*iter2)->rect,
(*iter2)->bufferStream->data(), (*iter2)->bufferStream->data(),
(*iter2)->bufferStream->length(), (*iter2)->bufferStream->length(),
*entry->cp))
*entry->server))
goto next; goto next;
} }
} }

+ 1
- 1
common/rfb/DecodeManager.h View File

Rect rect; Rect rect;
int encoding; int encoding;
Decoder* decoder; Decoder* decoder;
const ConnParams* cp;
const ServerParams* server;
ModifiablePixelBuffer* pb; ModifiablePixelBuffer* pb;
rdr::MemOutStream* bufferStream; rdr::MemOutStream* bufferStream;
Region affectedRegion; Region affectedRegion;

+ 2
- 2
common/rfb/Decoder.cxx View File

} }


void Decoder::getAffectedRegion(const Rect& rect, const void* buffer, void Decoder::getAffectedRegion(const Rect& rect, const void* buffer,
size_t buflen, const ConnParams& cp,
size_t buflen, const ServerParams& server,
Region* region) Region* region)
{ {
region->reset(rect); region->reset(rect);
bool Decoder::doRectsConflict(const Rect& rectA, const void* bufferA, bool Decoder::doRectsConflict(const Rect& rectA, const void* bufferA,
size_t buflenA, const Rect& rectB, size_t buflenA, const Rect& rectB,
const void* bufferB, size_t buflenB, const void* bufferB, size_t buflenB,
const ConnParams& cp)
const ServerParams& server)
{ {
return false; return false;
} }

+ 5
- 5
common/rfb/Decoder.h View File

} }


namespace rfb { namespace rfb {
class ConnParams;
class ServerParams;
class ModifiablePixelBuffer; class ModifiablePixelBuffer;
class Region; class Region;


// make it easier to decode. This function will always be called in // make it easier to decode. This function will always be called in
// a serial manner on the main thread. // a serial manner on the main thread.
virtual void readRect(const Rect& r, rdr::InStream* is, virtual void readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os)=0;
const ServerParams& server, rdr::OutStream* os)=0;


// These functions will be called from any of the worker threads. // These functions will be called from any of the worker threads.
// A lock will be held whilst these are called so it is safe to // A lock will be held whilst these are called so it is safe to
// be either read from or written do when decoding this rect. The // be either read from or written do when decoding this rect. The
// default implementation simply returns the given rectangle. // default implementation simply returns the given rectangle.
virtual void getAffectedRegion(const Rect& rect, const void* buffer, virtual void getAffectedRegion(const Rect& rect, const void* buffer,
size_t buflen, const ConnParams& cp,
size_t buflen, const ServerParams& server,
Region* region); Region* region);


// doesRectsConflict() determines if two rectangles must be decoded // doesRectsConflict() determines if two rectangles must be decoded
const Rect& rectB, const Rect& rectB,
const void* bufferB, const void* bufferB,
size_t buflenB, size_t buflenB,
const ConnParams& cp);
const ServerParams& server);


// decodeRect() decodes the given rectangle with data from the // decodeRect() decodes the given rectangle with data from the
// given buffer, onto the ModifiablePixelBuffer. The PixelFormat of // given buffer, onto the ModifiablePixelBuffer. The PixelFormat of
// the PixelBuffer might not match the ConnParams and it is up to // the PixelBuffer might not match the ConnParams and it is up to
// the decoder to do any necessary conversion. // the decoder to do any necessary conversion.
virtual void decodeRect(const Rect& r, const void* buffer, virtual void decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
size_t buflen, const ServerParams& server,
ModifiablePixelBuffer* pb)=0; ModifiablePixelBuffer* pb)=0;


public: public:

+ 23
- 19
common/rfb/EncodeManager.cxx View File



changed = changed_; changed = changed_;


if (!conn->client.supportsEncoding(encodingCopyRect))
changed.assign_union(copied);

/* /*
* We need to render the cursor seperately as it has its own * We need to render the cursor seperately as it has its own
* magical pixel buffer, so split it out from the changed region. * magical pixel buffer, so split it out from the changed region.
changed.assign_subtract(renderedCursor->getEffectiveRect()); changed.assign_subtract(renderedCursor->getEffectiveRect());
} }


if (conn->cp.supportsLastRect)
if (conn->client.supportsEncoding(pseudoEncodingLastRect))
nRects = 0xFFFF; nRects = 0xFFFF;
else { else {
nRects = copied.numRects(); nRects = copied.numRects();


conn->writer()->writeFramebufferUpdateStart(nRects); conn->writer()->writeFramebufferUpdateStart(nRects);


writeCopyRects(copied, copyDelta);
if (conn->client.supportsEncoding(encodingCopyRect))
writeCopyRects(copied, copyDelta);


/* /*
* We start by searching for solid rects, which are then removed * We start by searching for solid rects, which are then removed
* from the changed region. * from the changed region.
*/ */
if (conn->cp.supportsLastRect)
if (conn->client.supportsEncoding(pseudoEncodingLastRect))
writeSolidRects(&changed, pb); writeSolidRects(&changed, pb);


writeRects(changed, pb); writeRects(changed, pb);
solid = bitmap = bitmapRLE = encoderRaw; solid = bitmap = bitmapRLE = encoderRaw;
indexed = indexedRLE = fullColour = encoderRaw; indexed = indexedRLE = fullColour = encoderRaw;


allowJPEG = conn->cp.pf().bpp >= 16;
allowJPEG = conn->client.pf().bpp >= 16;
if (!allowLossy) { if (!allowLossy) {
if (encoders[encoderTightJPEG]->losslessQuality == -1) if (encoders[encoderTightJPEG]->losslessQuality == -1)
allowJPEG = false; allowJPEG = false;
} }


// JPEG is the only encoder that can reduce things to grayscale // JPEG is the only encoder that can reduce things to grayscale
if ((conn->cp.subsampling == subsampleGray) &&
if ((conn->client.subsampling == subsampleGray) &&
encoders[encoderTightJPEG]->isSupported() && allowLossy) { encoders[encoderTightJPEG]->isSupported() && allowLossy) {
solid = bitmap = bitmapRLE = encoderTightJPEG; solid = bitmap = bitmapRLE = encoderTightJPEG;
indexed = indexedRLE = fullColour = encoderTightJPEG; indexed = indexedRLE = fullColour = encoderTightJPEG;


encoder = encoders[*iter]; encoder = encoders[*iter];


encoder->setCompressLevel(conn->cp.compressLevel);
encoder->setCompressLevel(conn->client.compressLevel);


if (allowLossy) { if (allowLossy) {
encoder->setQualityLevel(conn->cp.qualityLevel);
encoder->setFineQualityLevel(conn->cp.fineQualityLevel,
conn->cp.subsampling);
encoder->setQualityLevel(conn->client.qualityLevel);
encoder->setFineQualityLevel(conn->client.fineQualityLevel,
conn->client.subsampling);
} else { } else {
int level = __rfbmax(conn->cp.qualityLevel,
int level = __rfbmax(conn->client.qualityLevel,
encoder->losslessQuality); encoder->losslessQuality);
encoder->setQualityLevel(level); encoder->setQualityLevel(level);
encoder->setFineQualityLevel(-1, subsampleUndefined); encoder->setFineQualityLevel(-1, subsampleUndefined);


stats[klass][activeType].rects++; stats[klass][activeType].rects++;
stats[klass][activeType].pixels += rect.area(); stats[klass][activeType].pixels += rect.area();
equiv = 12 + rect.area() * (conn->cp.pf().bpp/8);
equiv = 12 + rect.area() * (conn->client.pf().bpp/8);
stats[klass][activeType].equivalent += equiv; stats[klass][activeType].equivalent += equiv;


encoder = encoders[klass]; encoder = encoders[klass];


copyStats.rects++; copyStats.rects++;
copyStats.pixels += rect->area(); copyStats.pixels += rect->area();
equiv = 12 + rect->area() * (conn->cp.pf().bpp/8);
equiv = 12 + rect->area() * (conn->client.pf().bpp/8);
copyStats.equivalent += equiv; copyStats.equivalent += equiv;


conn->writer()->writeCopyRect(*rect, rect->tl.x - delta.x, conn->writer()->writeCopyRect(*rect, rect->tl.x - delta.x,
rdr::U32 _buffer2; rdr::U32 _buffer2;
rdr::U8* converted = (rdr::U8*)&_buffer2; rdr::U8* converted = (rdr::U8*)&_buffer2;


conn->cp.pf().bufferFromBuffer(converted, pb->getPF(),
conn->client.pf().bufferFromBuffer(converted, pb->getPF(),
colourValue, 1); colourValue, 1);


encoder->writeSolidRect(erp.width(), erp.height(), encoder->writeSolidRect(erp.width(), erp.height(),
conn->cp.pf(), converted);
conn->client.pf(), converted);
} }
endRect(); endRect();


// compression setting means spending less effort in building // compression setting means spending less effort in building
// a palette. It might be that they figured the increase in // a palette. It might be that they figured the increase in
// zlib setting compensated for the loss. // zlib setting compensated for the loss.
if (conn->cp.compressLevel == -1)
if (conn->client.compressLevel == -1)
divisor = 2 * 8; divisor = 2 * 8;
else else
divisor = conn->cp.compressLevel * 8;
divisor = conn->client.compressLevel * 8;
if (divisor < 4) if (divisor < 4)
divisor = 4; divisor = 4;




// Special exception inherited from the Tight encoder // Special exception inherited from the Tight encoder
if (activeEncoders[encoderFullColour] == encoderTightJPEG) { if (activeEncoders[encoderFullColour] == encoderTightJPEG) {
if ((conn->cp.compressLevel != -1) && (conn->cp.compressLevel < 2))
if ((conn->client.compressLevel != -1) && (conn->client.compressLevel < 2))
maxColours = 24; maxColours = 24;
else else
maxColours = 96; maxColours = 96;
int stride; int stride;


// Do wo need to convert the data? // Do wo need to convert the data?
if (convert && !conn->cp.pf().equal(pb->getPF())) {
convertedPixelBuffer.setPF(conn->cp.pf());
if (convert && !conn->client.pf().equal(pb->getPF())) {
convertedPixelBuffer.setPF(conn->client.pf());
convertedPixelBuffer.setSize(rect.width(), rect.height()); convertedPixelBuffer.setSize(rect.width(), rect.height());


buffer = pb->getBuffer(rect, &stride); buffer = pb->getBuffer(rect, &stride);

+ 5
- 5
common/rfb/HextileDecoder.cxx View File

#include <rdr/MemInStream.h> #include <rdr/MemInStream.h>
#include <rdr/OutStream.h> #include <rdr/OutStream.h>


#include <rfb/ConnParams.h>
#include <rfb/ServerParams.h>
#include <rfb/PixelBuffer.h> #include <rfb/PixelBuffer.h>
#include <rfb/HextileDecoder.h> #include <rfb/HextileDecoder.h>


} }


void HextileDecoder::readRect(const Rect& r, rdr::InStream* is, void HextileDecoder::readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os)
const ServerParams& server, rdr::OutStream* os)
{ {
Rect t; Rect t;
size_t bytesPerPixel; size_t bytesPerPixel;


bytesPerPixel = cp.pf().bpp/8;
bytesPerPixel = server.pf().bpp/8;


for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) { for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {


} }


void HextileDecoder::decodeRect(const Rect& r, const void* buffer, void HextileDecoder::decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
size_t buflen, const ServerParams& server,
ModifiablePixelBuffer* pb) ModifiablePixelBuffer* pb)
{ {
rdr::MemInStream is(buffer, buflen); rdr::MemInStream is(buffer, buflen);
const PixelFormat& pf = cp.pf();
const PixelFormat& pf = server.pf();
switch (pf.bpp) { switch (pf.bpp) {
case 8: hextileDecode8 (r, &is, pf, pb); break; case 8: hextileDecode8 (r, &is, pf, pb); break;
case 16: hextileDecode16(r, &is, pf, pb); break; case 16: hextileDecode16(r, &is, pf, pb); break;

+ 2
- 2
common/rfb/HextileDecoder.h View File

HextileDecoder(); HextileDecoder();
virtual ~HextileDecoder(); virtual ~HextileDecoder();
virtual void readRect(const Rect& r, rdr::InStream* is, virtual void readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os);
const ServerParams& server, rdr::OutStream* os);
virtual void decodeRect(const Rect& r, const void* buffer, virtual void decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
size_t buflen, const ServerParams& server,
ModifiablePixelBuffer* pb); ModifiablePixelBuffer* pb);
}; };
} }

+ 1
- 1
common/rfb/HextileEncoder.cxx View File



bool HextileEncoder::isSupported() bool HextileEncoder::isSupported()
{ {
return conn->cp.supportsEncoding(encodingHextile);
return conn->client.supportsEncoding(encodingHextile);
} }


void HextileEncoder::writeRect(const PixelBuffer* pb, const Palette& palette) void HextileEncoder::writeRect(const PixelBuffer* pb, const Palette& palette)

+ 1
- 1
common/rfb/JpegCompressor.cxx View File

#include <rdr/Exception.h> #include <rdr/Exception.h>
#include <rfb/Rect.h> #include <rfb/Rect.h>
#include <rfb/PixelFormat.h> #include <rfb/PixelFormat.h>
#include <rfb/ConnParams.h>
#include <rfb/ClientParams.h>


#include <stdio.h> #include <stdio.h>
extern "C" { extern "C" {

+ 10
- 1
common/rfb/Logger.cxx View File

char buf1[4096]; char buf1[4096];
vsnprintf(buf1, sizeof(buf1)-1, format, ap); vsnprintf(buf1, sizeof(buf1)-1, format, ap);
buf1[sizeof(buf1)-1] = 0; buf1[sizeof(buf1)-1] = 0;
write(level, logname, buf1);
char *buf = buf1;
while (true) {
char *end = strchr(buf, '\n');
if (end)
*end = '\0';
write(level, logname, buf);
if (!end)
break;
buf = end + 1;
}
} }


void void

+ 5
- 5
common/rfb/RREDecoder.cxx View File

#include <rdr/MemInStream.h> #include <rdr/MemInStream.h>
#include <rdr/OutStream.h> #include <rdr/OutStream.h>


#include <rfb/ConnParams.h>
#include <rfb/ServerParams.h>
#include <rfb/PixelBuffer.h> #include <rfb/PixelBuffer.h>
#include <rfb/RREDecoder.h> #include <rfb/RREDecoder.h>


} }


void RREDecoder::readRect(const Rect& r, rdr::InStream* is, void RREDecoder::readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os)
const ServerParams& server, rdr::OutStream* os)
{ {
rdr::U32 numRects; rdr::U32 numRects;


numRects = is->readU32(); numRects = is->readU32();
os->writeU32(numRects); os->writeU32(numRects);


os->copyBytes(is, cp.pf().bpp/8 + numRects * (cp.pf().bpp/8 + 8));
os->copyBytes(is, server.pf().bpp/8 + numRects * (server.pf().bpp/8 + 8));
} }


void RREDecoder::decodeRect(const Rect& r, const void* buffer, void RREDecoder::decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
size_t buflen, const ServerParams& server,
ModifiablePixelBuffer* pb) ModifiablePixelBuffer* pb)
{ {
rdr::MemInStream is(buffer, buflen); rdr::MemInStream is(buffer, buflen);
const PixelFormat& pf = cp.pf();
const PixelFormat& pf = server.pf();
switch (pf.bpp) { switch (pf.bpp) {
case 8: rreDecode8 (r, &is, pf, pb); break; case 8: rreDecode8 (r, &is, pf, pb); break;
case 16: rreDecode16(r, &is, pf, pb); break; case 16: rreDecode16(r, &is, pf, pb); break;

+ 2
- 2
common/rfb/RREDecoder.h View File

RREDecoder(); RREDecoder();
virtual ~RREDecoder(); virtual ~RREDecoder();
virtual void readRect(const Rect& r, rdr::InStream* is, virtual void readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os);
const ServerParams& server, rdr::OutStream* os);
virtual void decodeRect(const Rect& r, const void* buffer, virtual void decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
size_t buflen, const ServerParams& server,
ModifiablePixelBuffer* pb); ModifiablePixelBuffer* pb);
}; };
} }

+ 1
- 1
common/rfb/RREEncoder.cxx View File



bool RREEncoder::isSupported() bool RREEncoder::isSupported()
{ {
return conn->cp.supportsEncoding(encodingRRE);
return conn->client.supportsEncoding(encodingRRE);
} }


void RREEncoder::writeRect(const PixelBuffer* pb, const Palette& palette) void RREEncoder::writeRect(const PixelBuffer* pb, const Palette& palette)

+ 6
- 6
common/rfb/RawDecoder.cxx View File

#include <assert.h> #include <assert.h>


#include <rdr/OutStream.h> #include <rdr/OutStream.h>
#include <rfb/ConnParams.h>
#include <rfb/ServerParams.h>
#include <rfb/PixelBuffer.h> #include <rfb/PixelBuffer.h>
#include <rfb/RawDecoder.h> #include <rfb/RawDecoder.h>


} }


void RawDecoder::readRect(const Rect& r, rdr::InStream* is, void RawDecoder::readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os)
const ServerParams& server, rdr::OutStream* os)
{ {
os->copyBytes(is, r.area() * (cp.pf().bpp/8));
os->copyBytes(is, r.area() * (server.pf().bpp/8));
} }


void RawDecoder::decodeRect(const Rect& r, const void* buffer, void RawDecoder::decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
size_t buflen, const ServerParams& server,
ModifiablePixelBuffer* pb) ModifiablePixelBuffer* pb)
{ {
assert(buflen >= (size_t)r.area() * (cp.pf().bpp/8));
pb->imageRect(cp.pf(), r, buffer);
assert(buflen >= (size_t)r.area() * (server.pf().bpp/8));
pb->imageRect(server.pf(), r, buffer);
} }

+ 2
- 2
common/rfb/RawDecoder.h View File

RawDecoder(); RawDecoder();
virtual ~RawDecoder(); virtual ~RawDecoder();
virtual void readRect(const Rect& r, rdr::InStream* is, virtual void readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os);
const ServerParams& server, rdr::OutStream* os);
virtual void decodeRect(const Rect& r, const void* buffer, virtual void decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
size_t buflen, const ServerParams& server,
ModifiablePixelBuffer* pb); ModifiablePixelBuffer* pb);
}; };
} }

+ 44
- 26
common/rfb/SConnection.cxx View File

if (rfb::Server::protocol3_3) if (rfb::Server::protocol3_3)
defaultMinorVersion = 3; defaultMinorVersion = 3;


cp.setVersion(defaultMajorVersion, defaultMinorVersion);
client.setVersion(defaultMajorVersion, defaultMinorVersion);
} }


SConnection::~SConnection() SConnection::~SConnection()


void SConnection::initialiseProtocol() void SConnection::initialiseProtocol()
{ {
cp.writeVersion(os);
char str[13];

sprintf(str, "RFB %03d.%03d\n", defaultMajorVersion, defaultMinorVersion);
os->writeBytes(str, 12);
os->flush();

state_ = RFBSTATE_PROTOCOL_VERSION; state_ = RFBSTATE_PROTOCOL_VERSION;
} }




void SConnection::processVersionMsg() void SConnection::processVersionMsg()
{ {
char verStr[13];
int majorVersion;
int minorVersion;

vlog.debug("reading protocol version"); vlog.debug("reading protocol version");
bool done;
if (!cp.readVersion(is, &done)) {

if (!is->checkNoWait(12))
return;

is->readBytes(verStr, 12);
verStr[12] = '\0';

if (sscanf(verStr, "RFB %03d.%03d\n",
&majorVersion, &minorVersion) != 2) {
state_ = RFBSTATE_INVALID; state_ = RFBSTATE_INVALID;
throw Exception("reading version failed: not an RFB client?"); throw Exception("reading version failed: not an RFB client?");
} }
if (!done) return;

client.setVersion(majorVersion, minorVersion);


vlog.info("Client needs protocol version %d.%d", vlog.info("Client needs protocol version %d.%d",
cp.majorVersion, cp.minorVersion);
client.majorVersion, client.minorVersion);


if (cp.majorVersion != 3) {
if (client.majorVersion != 3) {
// unknown protocol version // unknown protocol version
throwConnFailedException("Client needs protocol version %d.%d, server has %d.%d", throwConnFailedException("Client needs protocol version %d.%d, server has %d.%d",
cp.majorVersion, cp.minorVersion,
client.majorVersion, client.minorVersion,
defaultMajorVersion, defaultMinorVersion); defaultMajorVersion, defaultMinorVersion);
} }


if (cp.minorVersion != 3 && cp.minorVersion != 7 && cp.minorVersion != 8) {
if (client.minorVersion != 3 && client.minorVersion != 7 && client.minorVersion != 8) {
vlog.error("Client uses unofficial protocol version %d.%d", vlog.error("Client uses unofficial protocol version %d.%d",
cp.majorVersion,cp.minorVersion);
if (cp.minorVersion >= 8)
cp.minorVersion = 8;
else if (cp.minorVersion == 7)
cp.minorVersion = 7;
client.majorVersion,client.minorVersion);
if (client.minorVersion >= 8)
client.minorVersion = 8;
else if (client.minorVersion == 7)
client.minorVersion = 7;
else else
cp.minorVersion = 3;
client.minorVersion = 3;
vlog.error("Assuming compatibility with version %d.%d", vlog.error("Assuming compatibility with version %d.%d",
cp.majorVersion,cp.minorVersion);
client.majorVersion,client.minorVersion);
} }


versionReceived(); versionReceived();
std::list<rdr::U8>::iterator i; std::list<rdr::U8>::iterator i;
secTypes = security.GetEnabledSecTypes(); secTypes = security.GetEnabledSecTypes();


if (cp.isVersion(3,3)) {
if (client.isVersion(3,3)) {


// cope with legacy 3.3 client only if "no authentication" or "vnc // cope with legacy 3.3 client only if "no authentication" or "vnc
// authentication" is supported. // authentication" is supported.
} }
if (i == secTypes.end()) { if (i == secTypes.end()) {
throwConnFailedException("No supported security type for %d.%d client", throwConnFailedException("No supported security type for %d.%d client",
cp.majorVersion, cp.minorVersion);
client.majorVersion, client.minorVersion);
} }


os->writeU32(*i); os->writeU32(*i);
} catch (AuthFailureException& e) { } catch (AuthFailureException& e) {
vlog.error("AuthFailureException: %s", e.str()); vlog.error("AuthFailureException: %s", e.str());
os->writeU32(secResultFailed); os->writeU32(secResultFailed);
if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
if (!client.beforeVersion(3,8)) // 3.8 onwards have failure message
os->writeString(e.str()); os->writeString(e.str());
os->flush(); os->flush();
throw; throw;
vlog.info("Connection failed: %s", str); vlog.info("Connection failed: %s", str);


if (state_ == RFBSTATE_PROTOCOL_VERSION) { if (state_ == RFBSTATE_PROTOCOL_VERSION) {
if (cp.majorVersion == 3 && cp.minorVersion == 3) {
if (client.majorVersion == 3 && client.minorVersion == 3) {
os->writeU32(0); os->writeU32(0);
os->writeString(str); os->writeString(str);
os->flush(); os->flush();
if (state_ != RFBSTATE_QUERYING) if (state_ != RFBSTATE_QUERYING)
throw Exception("SConnection::approveConnection: invalid state"); throw Exception("SConnection::approveConnection: invalid state");


if (!cp.beforeVersion(3,8) || ssecurity->getType() != secTypeNone) {
if (!client.beforeVersion(3,8) || ssecurity->getType() != secTypeNone) {
if (accept) { if (accept) {
os->writeU32(secResultOK); os->writeU32(secResultOK);
} else { } else {
os->writeU32(secResultFailed); os->writeU32(secResultFailed);
if (!cp.beforeVersion(3,8)) { // 3.8 onwards have failure message
if (!client.beforeVersion(3,8)) { // 3.8 onwards have failure message
if (reason) if (reason)
os->writeString(reason); os->writeString(reason);
else else
if (accept) { if (accept) {
state_ = RFBSTATE_INITIALISATION; state_ = RFBSTATE_INITIALISATION;
reader_ = new SMsgReader(this, is); reader_ = new SMsgReader(this, is);
writer_ = new SMsgWriter(&cp, os);
writer_ = new SMsgWriter(&client, os);
authSuccess(); authSuccess();
} else { } else {
state_ = RFBSTATE_INVALID; state_ = RFBSTATE_INVALID;


void SConnection::clientInit(bool shared) void SConnection::clientInit(bool shared)
{ {
writer_->writeServerInit();
writer_->writeServerInit(client.width(), client.height(),
client.pf(), client.name());
state_ = RFBSTATE_NORMAL; state_ = RFBSTATE_NORMAL;
} }


{ {
if (!readyForSetColourMapEntries) { if (!readyForSetColourMapEntries) {
readyForSetColourMapEntries = true; readyForSetColourMapEntries = true;
if (!cp.pf().trueColour) {
if (!client.pf().trueColour) {
writeFakeColourMap(); writeFakeColourMap();
} }
} }
rdr::U16 red[256], green[256], blue[256]; rdr::U16 red[256], green[256], blue[256];


for (i = 0;i < 256;i++) for (i = 0;i < 256;i++)
cp.pf().rgbFromPixel(i, &red[i], &green[i], &blue[i]);
client.pf().rgbFromPixel(i, &red[i], &green[i], &blue[i]);


writer()->writeSetColourMapEntries(0, 256, red, green, blue); writer()->writeSetColourMapEntries(0, 256, red, green, blue);
} }

+ 11
- 19
common/rfb/SMsgHandler.cxx View File

#include <rfb/Exception.h> #include <rfb/Exception.h>
#include <rfb/SMsgHandler.h> #include <rfb/SMsgHandler.h>
#include <rfb/ScreenSet.h> #include <rfb/ScreenSet.h>
#include <rfb/encodings.h>


using namespace rfb; using namespace rfb;




void SMsgHandler::setPixelFormat(const PixelFormat& pf) void SMsgHandler::setPixelFormat(const PixelFormat& pf)
{ {
cp.setPF(pf);
client.setPF(pf);
} }


void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings) void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings)
bool firstFence, firstContinuousUpdates, firstLEDState, bool firstFence, firstContinuousUpdates, firstLEDState,
firstQEMUKeyEvent; firstQEMUKeyEvent;


firstFence = !cp.supportsFence;
firstContinuousUpdates = !cp.supportsContinuousUpdates;
firstLEDState = !cp.supportsLEDState;
firstQEMUKeyEvent = !cp.supportsQEMUKeyEvent;
firstFence = !client.supportsFence();
firstContinuousUpdates = !client.supportsContinuousUpdates();
firstLEDState = !client.supportsLEDState();
firstQEMUKeyEvent = !client.supportsEncoding(pseudoEncodingQEMUKeyEvent);


cp.setEncodings(nEncodings, encodings);
client.setEncodings(nEncodings, encodings);


supportsLocalCursor(); supportsLocalCursor();


if (cp.supportsFence && firstFence)
if (client.supportsFence() && firstFence)
supportsFence(); supportsFence();
if (cp.supportsContinuousUpdates && firstContinuousUpdates)
if (client.supportsContinuousUpdates() && firstContinuousUpdates)
supportsContinuousUpdates(); supportsContinuousUpdates();
if (cp.supportsLEDState && firstLEDState)
if (client.supportsLEDState() && firstLEDState)
supportsLEDState(); supportsLEDState();
if (cp.supportsQEMUKeyEvent && firstQEMUKeyEvent)
if (client.supportsEncoding(pseudoEncodingQEMUKeyEvent) && firstQEMUKeyEvent)
supportsQEMUKeyEvent(); supportsQEMUKeyEvent();
} }


void SMsgHandler::supportsQEMUKeyEvent() void SMsgHandler::supportsQEMUKeyEvent()
{ {
} }

void SMsgHandler::setDesktopSize(int fb_width, int fb_height,
const ScreenSet& layout)
{
cp.width = fb_width;
cp.height = fb_height;
cp.screenLayout = layout;
}


+ 4
- 4
common/rfb/SMsgHandler.h View File



#include <rdr/types.h> #include <rdr/types.h>
#include <rfb/PixelFormat.h> #include <rfb/PixelFormat.h>
#include <rfb/ConnParams.h>
#include <rfb/ClientParams.h>
#include <rfb/InputHandler.h> #include <rfb/InputHandler.h>
#include <rfb/ScreenSet.h> #include <rfb/ScreenSet.h>




// The following methods are called as corresponding messages are read. A // The following methods are called as corresponding messages are read. A
// derived class should override these methods as desired. Note that for // derived class should override these methods as desired. Note that for
// the setPixelFormat(), setEncodings() and setDesktopSize() methods, a
// derived class must call on to SMsgHandler's methods.
// the setPixelFormat(), and setEncodings() methods, a derived class must
// call on to SMsgHandler's methods.


virtual void clientInit(bool shared); virtual void clientInit(bool shared);


// handler will send a pseudo-rect back, signalling server support. // handler will send a pseudo-rect back, signalling server support.
virtual void supportsQEMUKeyEvent(); virtual void supportsQEMUKeyEvent();


ConnParams cp;
ClientParams client;
}; };
} }
#endif #endif

+ 105
- 170
common/rfb/SMsgWriter.cxx View File

#include <rfb/msgTypes.h> #include <rfb/msgTypes.h>
#include <rfb/fenceTypes.h> #include <rfb/fenceTypes.h>
#include <rfb/Exception.h> #include <rfb/Exception.h>
#include <rfb/ConnParams.h>
#include <rfb/ClientParams.h>
#include <rfb/UpdateTracker.h> #include <rfb/UpdateTracker.h>
#include <rfb/Encoder.h> #include <rfb/Encoder.h>
#include <rfb/SMsgWriter.h> #include <rfb/SMsgWriter.h>


static LogWriter vlog("SMsgWriter"); static LogWriter vlog("SMsgWriter");


SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
: cp(cp_), os(os_),
SMsgWriter::SMsgWriter(ClientParams* client_, rdr::OutStream* os_)
: client(client_), os(os_),
nRectsInUpdate(0), nRectsInHeader(0), nRectsInUpdate(0), nRectsInHeader(0),
needSetDesktopSize(false), needExtendedDesktopSize(false),
needSetDesktopName(false), needSetCursor(false),
needSetXCursor(false), needSetCursorWithAlpha(false),
needSetDesktopName(false), needCursor(false),
needLEDState(false), needQEMUKeyEvent(false) needLEDState(false), needQEMUKeyEvent(false)
{ {
} }
{ {
} }


void SMsgWriter::writeServerInit()
void SMsgWriter::writeServerInit(rdr::U16 width, rdr::U16 height,
const PixelFormat& pf, const char* name)
{ {
os->writeU16(cp->width);
os->writeU16(cp->height);
cp->pf().write(os);
os->writeString(cp->name());
os->writeU16(width);
os->writeU16(height);
pf.write(os);
os->writeString(name);
endMsg(); endMsg();
} }




void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[]) void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
{ {
if (!cp->supportsFence)
if (!client->supportsEncoding(pseudoEncodingFence))
throw Exception("Client does not support fences"); throw Exception("Client does not support fences");
if (len > 64) if (len > 64)
throw Exception("Too large fence payload"); throw Exception("Too large fence payload");


void SMsgWriter::writeEndOfContinuousUpdates() void SMsgWriter::writeEndOfContinuousUpdates()
{ {
if (!cp->supportsContinuousUpdates)
if (!client->supportsEncoding(pseudoEncodingContinuousUpdates))
throw Exception("Client does not support continuous updates"); throw Exception("Client does not support continuous updates");


startMsg(msgTypeEndOfContinuousUpdates); startMsg(msgTypeEndOfContinuousUpdates);
endMsg(); endMsg();
} }


bool SMsgWriter::writeSetDesktopSize() {
if (!cp->supportsDesktopResize)
return false;

needSetDesktopSize = true;

return true;
}

bool SMsgWriter::writeExtendedDesktopSize() {
if (!cp->supportsExtendedDesktopSize)
return false;

needExtendedDesktopSize = true;

return true;
}

bool SMsgWriter::writeExtendedDesktopSize(rdr::U16 reason, rdr::U16 result,
int fb_width, int fb_height,
const ScreenSet& layout) {
void SMsgWriter::writeDesktopSize(rdr::U16 reason, rdr::U16 result)
{
ExtendedDesktopSizeMsg msg; ExtendedDesktopSizeMsg msg;


if (!cp->supportsExtendedDesktopSize)
return false;
if (!client->supportsEncoding(pseudoEncodingDesktopSize) &&
!client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
throw Exception("Client does not support desktop size changes");


msg.reason = reason; msg.reason = reason;
msg.result = result; msg.result = result;
msg.fb_width = fb_width;
msg.fb_height = fb_height;
msg.layout = layout;


extendedDesktopSizeMsgs.push_back(msg); extendedDesktopSizeMsgs.push_back(msg);

return true;
} }


bool SMsgWriter::writeSetDesktopName() {
if (!cp->supportsDesktopRename)
return false;

needSetDesktopName = true;

return true;
}

bool SMsgWriter::writeSetCursor()
{
if (!cp->supportsLocalCursor)
return false;

needSetCursor = true;

return true;
}

bool SMsgWriter::writeSetXCursor()
void SMsgWriter::writeSetDesktopName()
{ {
if (!cp->supportsLocalXCursor)
return false;

needSetXCursor = true;
if (!client->supportsEncoding(pseudoEncodingDesktopName))
throw Exception("Client does not support desktop name changes");


return true;
needSetDesktopName = true;
} }


bool SMsgWriter::writeSetCursorWithAlpha()
void SMsgWriter::writeCursor()
{ {
if (!cp->supportsLocalCursorWithAlpha)
return false;
if (!client->supportsEncoding(pseudoEncodingCursor) &&
!client->supportsEncoding(pseudoEncodingXCursor) &&
!client->supportsEncoding(pseudoEncodingCursorWithAlpha))
throw Exception("Client does not support local cursor");


needSetCursorWithAlpha = true;

return true;
needCursor = true;
} }


bool SMsgWriter::writeLEDState()
void SMsgWriter::writeLEDState()
{ {
if (!cp->supportsLEDState)
return false;
if (cp->ledState() == ledUnknown)
return false;
if (!client->supportsEncoding(pseudoEncodingLEDState))
throw Exception("Client does not support LED state");
if (client->ledState() == ledUnknown)
throw Exception("Server has not specified LED state");


needLEDState = true; needLEDState = true;

return true;
} }


bool SMsgWriter::writeQEMUKeyEvent()
void SMsgWriter::writeQEMUKeyEvent()
{ {
if (!cp->supportsQEMUKeyEvent)
return false;
if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent))
throw Exception("Client does not support QEMU key events");


needQEMUKeyEvent = true; needQEMUKeyEvent = true;

return true;
} }


bool SMsgWriter::needFakeUpdate() bool SMsgWriter::needFakeUpdate()
{ {
if (needSetDesktopName) if (needSetDesktopName)
return true; return true;
if (needSetCursor || needSetXCursor || needSetCursorWithAlpha)
if (needCursor)
return true; return true;
if (needLEDState) if (needLEDState)
return true; return true;


bool SMsgWriter::needNoDataUpdate() bool SMsgWriter::needNoDataUpdate()
{ {
if (needSetDesktopSize)
return true;
if (needExtendedDesktopSize || !extendedDesktopSizeMsgs.empty())
if (!extendedDesktopSizeMsgs.empty())
return true; return true;


return false; return false;


nRects = 0; nRects = 0;


if (needSetDesktopSize)
nRects++;
if (needExtendedDesktopSize)
nRects++;
if (!extendedDesktopSizeMsgs.empty())
nRects += extendedDesktopSizeMsgs.size();
if (!extendedDesktopSizeMsgs.empty()) {
if (client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
nRects += extendedDesktopSizeMsgs.size();
else
nRects++;
}


writeFramebufferUpdateStart(nRects); writeFramebufferUpdateStart(nRects);
writeNoDataRects(); writeNoDataRects();
if (nRects != 0xFFFF) { if (nRects != 0xFFFF) {
if (needSetDesktopName) if (needSetDesktopName)
nRects++; nRects++;
if (needSetCursor)
nRects++;
if (needSetXCursor)
nRects++;
if (needSetCursorWithAlpha)
if (needCursor)
nRects++; nRects++;
if (needLEDState) if (needLEDState)
nRects++; nRects++;


void SMsgWriter::writePseudoRects() void SMsgWriter::writePseudoRects()
{ {
if (needSetCursor) {
const Cursor& cursor = cp->cursor();

rdr::U8Array data(cursor.width()*cursor.height() * (cp->pf().bpp/8));
rdr::U8Array mask(cursor.getMask());

const rdr::U8* in;
rdr::U8* out;

in = cursor.getBuffer();
out = data.buf;
for (int i = 0;i < cursor.width()*cursor.height();i++) {
cp->pf().bufferFromRGB(out, in, 1);
in += 4;
out += cp->pf().bpp/8;
if (needCursor) {
const Cursor& cursor = client->cursor();

if (client->supportsEncoding(pseudoEncodingCursorWithAlpha)) {
writeSetCursorWithAlphaRect(cursor.width(), cursor.height(),
cursor.hotspot().x, cursor.hotspot().y,
cursor.getBuffer());
} else if (client->supportsEncoding(pseudoEncodingCursor)) {
rdr::U8Array data(cursor.width()*cursor.height() * (client->pf().bpp/8));
rdr::U8Array mask(cursor.getMask());

const rdr::U8* in;
rdr::U8* out;

in = cursor.getBuffer();
out = data.buf;
for (int i = 0;i < cursor.width()*cursor.height();i++) {
client->pf().bufferFromRGB(out, in, 1);
in += 4;
out += client->pf().bpp/8;
}

writeSetCursorRect(cursor.width(), cursor.height(),
cursor.hotspot().x, cursor.hotspot().y,
data.buf, mask.buf);
} else if (client->supportsEncoding(pseudoEncodingXCursor)) {
rdr::U8Array bitmap(cursor.getBitmap());
rdr::U8Array mask(cursor.getMask());

writeSetXCursorRect(cursor.width(), cursor.height(),
cursor.hotspot().x, cursor.hotspot().y,
bitmap.buf, mask.buf);
} else {
throw Exception("Client does not support local cursor");
} }


writeSetCursorRect(cursor.width(), cursor.height(),
cursor.hotspot().x, cursor.hotspot().y,
data.buf, mask.buf);
needSetCursor = false;
}

if (needSetXCursor) {
const Cursor& cursor = cp->cursor();
rdr::U8Array bitmap(cursor.getBitmap());
rdr::U8Array mask(cursor.getMask());

writeSetXCursorRect(cursor.width(), cursor.height(),
cursor.hotspot().x, cursor.hotspot().y,
bitmap.buf, mask.buf);
needSetXCursor = false;
}

if (needSetCursorWithAlpha) {
const Cursor& cursor = cp->cursor();

writeSetCursorWithAlphaRect(cursor.width(), cursor.height(),
cursor.hotspot().x, cursor.hotspot().y,
cursor.getBuffer());
needSetCursorWithAlpha = false;
needCursor = false;
} }


if (needSetDesktopName) { if (needSetDesktopName) {
writeSetDesktopNameRect(cp->name());
writeSetDesktopNameRect(client->name());
needSetDesktopName = false; needSetDesktopName = false;
} }


if (needLEDState) { if (needLEDState) {
writeLEDStateRect(cp->ledState());
writeLEDStateRect(client->ledState());
needLEDState = false; needLEDState = false;
} }




void SMsgWriter::writeNoDataRects() void SMsgWriter::writeNoDataRects()
{ {
// Start with specific ExtendedDesktopSize messages
if (!extendedDesktopSizeMsgs.empty()) { if (!extendedDesktopSizeMsgs.empty()) {
std::list<ExtendedDesktopSizeMsg>::const_iterator ri;

for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
writeExtendedDesktopSizeRect(ri->reason, ri->result,
ri->fb_width, ri->fb_height, ri->layout);
if (client->supportsEncoding(pseudoEncodingExtendedDesktopSize)) {
std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
// FIXME: We can probably skip multiple reasonServer entries
writeExtendedDesktopSizeRect(ri->reason, ri->result,
client->width(), client->height(),
client->screenLayout());
}
} else if (client->supportsEncoding(pseudoEncodingDesktopSize)) {
// Some clients assume this is the last rectangle so don't send anything
// more after this
writeSetDesktopSizeRect(client->width(), client->height());
} else {
throw Exception("Client does not support desktop size changes");
} }


extendedDesktopSizeMsgs.clear(); extendedDesktopSizeMsgs.clear();
} }

// Send this before SetDesktopSize to make life easier on the clients
if (needExtendedDesktopSize) {
writeExtendedDesktopSizeRect(0, 0, cp->width, cp->height,
cp->screenLayout);
needExtendedDesktopSize = false;
}

// Some clients assume this is the last rectangle so don't send anything
// more after this
if (needSetDesktopSize) {
writeSetDesktopSizeRect(cp->width, cp->height);
needSetDesktopSize = false;
}
} }


void SMsgWriter::writeSetDesktopSizeRect(int width, int height) void SMsgWriter::writeSetDesktopSizeRect(int width, int height)
{ {
if (!cp->supportsDesktopResize)
if (!client->supportsEncoding(pseudoEncodingDesktopSize))
throw Exception("Client does not support desktop resize"); throw Exception("Client does not support desktop resize");
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
throw Exception("SMsgWriter::writeSetDesktopSizeRect: nRects out of sync"); throw Exception("SMsgWriter::writeSetDesktopSizeRect: nRects out of sync");
{ {
ScreenSet::const_iterator si; ScreenSet::const_iterator si;


if (!cp->supportsExtendedDesktopSize)
if (!client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
throw Exception("Client does not support extended desktop resize"); throw Exception("Client does not support extended desktop resize");
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
throw Exception("SMsgWriter::writeExtendedDesktopSizeRect: nRects out of sync"); throw Exception("SMsgWriter::writeExtendedDesktopSizeRect: nRects out of sync");


void SMsgWriter::writeSetDesktopNameRect(const char *name) void SMsgWriter::writeSetDesktopNameRect(const char *name)
{ {
if (!cp->supportsDesktopRename)
if (!client->supportsEncoding(pseudoEncodingDesktopName))
throw Exception("Client does not support desktop rename"); throw Exception("Client does not support desktop rename");
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
throw Exception("SMsgWriter::writeSetDesktopNameRect: nRects out of sync"); throw Exception("SMsgWriter::writeSetDesktopNameRect: nRects out of sync");
int hotspotX, int hotspotY, int hotspotX, int hotspotY,
const void* data, const void* mask) const void* data, const void* mask)
{ {
if (!cp->supportsLocalCursor)
if (!client->supportsEncoding(pseudoEncodingCursor))
throw Exception("Client does not support local cursors"); throw Exception("Client does not support local cursors");
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
throw Exception("SMsgWriter::writeSetCursorRect: nRects out of sync"); throw Exception("SMsgWriter::writeSetCursorRect: nRects out of sync");
os->writeU16(width); os->writeU16(width);
os->writeU16(height); os->writeU16(height);
os->writeU32(pseudoEncodingCursor); os->writeU32(pseudoEncodingCursor);
os->writeBytes(data, width * height * (cp->pf().bpp/8));
os->writeBytes(data, width * height * (client->pf().bpp/8));
os->writeBytes(mask, (width+7)/8 * height); os->writeBytes(mask, (width+7)/8 * height);
} }


int hotspotX, int hotspotY, int hotspotX, int hotspotY,
const void* data, const void* mask) const void* data, const void* mask)
{ {
if (!cp->supportsLocalXCursor)
if (!client->supportsEncoding(pseudoEncodingXCursor))
throw Exception("Client does not support local cursors"); throw Exception("Client does not support local cursors");
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
throw Exception("SMsgWriter::writeSetXCursorRect: nRects out of sync"); throw Exception("SMsgWriter::writeSetXCursorRect: nRects out of sync");
int hotspotX, int hotspotY, int hotspotX, int hotspotY,
const rdr::U8* data) const rdr::U8* data)
{ {
if (!cp->supportsLocalCursorWithAlpha)
if (!client->supportsEncoding(pseudoEncodingCursorWithAlpha))
throw Exception("Client does not support local cursors"); throw Exception("Client does not support local cursors");
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
throw Exception("SMsgWriter::writeSetCursorWithAlphaRect: nRects out of sync"); throw Exception("SMsgWriter::writeSetCursorWithAlphaRect: nRects out of sync");


void SMsgWriter::writeLEDStateRect(rdr::U8 state) void SMsgWriter::writeLEDStateRect(rdr::U8 state)
{ {
if (!cp->supportsLEDState)
if (!client->supportsEncoding(pseudoEncodingLEDState))
throw Exception("Client does not support LED state updates"); throw Exception("Client does not support LED state updates");
if (cp->ledState() == ledUnknown)
if (client->ledState() == ledUnknown)
throw Exception("Server does not support LED state updates"); throw Exception("Server does not support LED state updates");
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
throw Exception("SMsgWriter::writeLEDStateRect: nRects out of sync"); throw Exception("SMsgWriter::writeLEDStateRect: nRects out of sync");


void SMsgWriter::writeQEMUKeyEventRect() void SMsgWriter::writeQEMUKeyEventRect()
{ {
if (!cp->supportsQEMUKeyEvent)
if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent))
throw Exception("Client does not support QEMU extended key events"); throw Exception("Client does not support QEMU extended key events");
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
throw Exception("SMsgWriter::writeQEMUKeyEventRect: nRects out of sync"); throw Exception("SMsgWriter::writeQEMUKeyEventRect: nRects out of sync");

+ 13
- 26
common/rfb/SMsgWriter.h View File



namespace rfb { namespace rfb {


class ConnParams;
class ClientParams;
class PixelFormat;
struct ScreenSet; struct ScreenSet;


class SMsgWriter { class SMsgWriter {
public: public:
SMsgWriter(ConnParams* cp, rdr::OutStream* os);
SMsgWriter(ClientParams* client, rdr::OutStream* os);
virtual ~SMsgWriter(); virtual ~SMsgWriter();


// writeServerInit() must only be called at the appropriate time in the // writeServerInit() must only be called at the appropriate time in the
// protocol initialisation. // protocol initialisation.
void writeServerInit();
void writeServerInit(rdr::U16 width, rdr::U16 height,
const PixelFormat& pf, const char* name);


// Methods to write normal protocol messages // Methods to write normal protocol messages


// updates mode. // updates mode.
void writeEndOfContinuousUpdates(); void writeEndOfContinuousUpdates();


// writeSetDesktopSize() won't actually write immediately, but will
// writeDesktopSize() won't actually write immediately, but will
// write the relevant pseudo-rectangle as part of the next update. // write the relevant pseudo-rectangle as part of the next update.
bool writeSetDesktopSize();
// Same thing for the extended version. The first version queues up a
// generic update of the current server state, but the second queues a
// specific message.
bool writeExtendedDesktopSize();
bool writeExtendedDesktopSize(rdr::U16 reason, rdr::U16 result,
int fb_width, int fb_height,
const ScreenSet& layout);
void writeDesktopSize(rdr::U16 reason, rdr::U16 result=0);


bool writeSetDesktopName();
void writeSetDesktopName();


// Like setDesktopSize, we can't just write out a cursor message // Like setDesktopSize, we can't just write out a cursor message
// immediately. // immediately.
bool writeSetCursor();
bool writeSetXCursor();
bool writeSetCursorWithAlpha();
void writeCursor();


// Same for LED state message // Same for LED state message
bool writeLEDState();
void writeLEDState();


// And QEMU keyboard event handshake // And QEMU keyboard event handshake
bool writeQEMUKeyEvent();
void writeQEMUKeyEvent();


// needFakeUpdate() returns true when an immediate update is needed in // needFakeUpdate() returns true when an immediate update is needed in
// order to flush out pseudo-rectangles to the client. // order to flush out pseudo-rectangles to the client.
void writeLEDStateRect(rdr::U8 state); void writeLEDStateRect(rdr::U8 state);
void writeQEMUKeyEventRect(); void writeQEMUKeyEventRect();


ConnParams* cp;
ClientParams* client;
rdr::OutStream* os; rdr::OutStream* os;


int nRectsInUpdate; int nRectsInUpdate;
int nRectsInHeader; int nRectsInHeader;


bool needSetDesktopSize;
bool needExtendedDesktopSize;
bool needSetDesktopName; bool needSetDesktopName;
bool needSetCursor;
bool needSetXCursor;
bool needSetCursorWithAlpha;
bool needCursor;
bool needLEDState; bool needLEDState;
bool needQEMUKeyEvent; bool needQEMUKeyEvent;


typedef struct { typedef struct {
rdr::U16 reason, result; rdr::U16 reason, result;
int fb_width, fb_height;
ScreenSet layout;
} ExtendedDesktopSizeMsg; } ExtendedDesktopSizeMsg;


std::list<ExtendedDesktopSizeMsg> extendedDesktopSizeMsgs; std::list<ExtendedDesktopSizeMsg> extendedDesktopSizeMsgs;

+ 84
- 0
common/rfb/ServerParams.cxx View File

/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
* Copyright 2014-2018 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include <rfb/Exception.h>
#include <rfb/ledStates.h>
#include <rfb/ServerParams.h>

using namespace rfb;

ServerParams::ServerParams()
: majorVersion(0), minorVersion(0),
supportsQEMUKeyEvent(false),
supportsSetDesktopSize(false), supportsFence(false),
supportsContinuousUpdates(false),
width_(0), height_(0), name_(0),
ledState_(ledUnknown)
{
setName("");
cursor_ = new Cursor(0, 0, Point(), NULL);
}

ServerParams::~ServerParams()
{
delete [] name_;
delete cursor_;
}

void ServerParams::setDimensions(int width, int height)
{
ScreenSet layout;
layout.add_screen(rfb::Screen(0, 0, 0, width, height, 0));
setDimensions(width, height, layout);
}

void ServerParams::setDimensions(int width, int height, const ScreenSet& layout)
{
if (!layout.validate(width, height))
throw Exception("Attempted to configure an invalid screen layout");

width_ = width;
height_ = height;
screenLayout_ = layout;
}

void ServerParams::setPF(const PixelFormat& pf)
{
pf_ = pf;

if (pf.bpp != 8 && pf.bpp != 16 && pf.bpp != 32)
throw Exception("setPF: not 8, 16 or 32 bpp?");
}

void ServerParams::setName(const char* name)
{
delete [] name_;
name_ = strDup(name);
}

void ServerParams::setCursor(const Cursor& other)
{
delete cursor_;
cursor_ = new Cursor(other);
}

void ServerParams::setLEDState(unsigned int state)
{
ledState_ = state;
}

+ 89
- 0
common/rfb/ServerParams.h View File

/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2014-2018 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
//
// ServerParams - structure describing the current state of the remote server
//

#ifndef __RFB_SERVERPARAMS_H__
#define __RFB_SERVERPARAMS_H__

#include <rfb/Cursor.h>
#include <rfb/PixelFormat.h>
#include <rfb/ScreenSet.h>

namespace rfb {

class ServerParams {
public:
ServerParams();
~ServerParams();

int majorVersion;
int minorVersion;

void setVersion(int major, int minor) {
majorVersion = major; minorVersion = minor;
}
bool isVersion(int major, int minor) const {
return majorVersion == major && minorVersion == minor;
}
bool beforeVersion(int major, int minor) const {
return (majorVersion < major ||
(majorVersion == major && minorVersion < minor));
}
bool afterVersion(int major, int minor) const {
return !beforeVersion(major,minor+1);
}

const int width() const { return width_; }
const int height() const { return height_; }
const ScreenSet& screenLayout() const { return screenLayout_; }
void setDimensions(int width, int height);
void setDimensions(int width, int height, const ScreenSet& layout);

const PixelFormat& pf() const { return pf_; }
void setPF(const PixelFormat& pf);

const char* name() const { return name_; }
void setName(const char* name);

const Cursor& cursor() const { return *cursor_; }
void setCursor(const Cursor& cursor);

unsigned int ledState() { return ledState_; }
void setLEDState(unsigned int state);

bool supportsQEMUKeyEvent;
bool supportsSetDesktopSize;
bool supportsFence;
bool supportsContinuousUpdates;

private:

int width_;
int height_;
ScreenSet screenLayout_;

PixelFormat pf_;
char* name_;
Cursor* cursor_;
unsigned int ledState_;
};
}
#endif

+ 12
- 12
common/rfb/TightDecoder.cxx View File

#include <rdr/MemInStream.h> #include <rdr/MemInStream.h>
#include <rdr/OutStream.h> #include <rdr/OutStream.h>


#include <rfb/ConnParams.h>
#include <rfb/ServerParams.h>
#include <rfb/Exception.h> #include <rfb/Exception.h>
#include <rfb/PixelBuffer.h> #include <rfb/PixelBuffer.h>
#include <rfb/TightConstants.h> #include <rfb/TightConstants.h>
} }


void TightDecoder::readRect(const Rect& r, rdr::InStream* is, void TightDecoder::readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os)
const ServerParams& server, rdr::OutStream* os)
{ {
rdr::U8 comp_ctl; rdr::U8 comp_ctl;




// "Fill" compression type. // "Fill" compression type.
if (comp_ctl == tightFill) { if (comp_ctl == tightFill) {
if (cp.pf().is888())
if (server.pf().is888())
os->copyBytes(is, 3); os->copyBytes(is, 3);
else else
os->copyBytes(is, cp.pf().bpp/8);
os->copyBytes(is, server.pf().bpp/8);
return; return;
} }


palSize = is->readU8() + 1; palSize = is->readU8() + 1;
os->writeU8(palSize - 1); os->writeU8(palSize - 1);


if (cp.pf().is888())
if (server.pf().is888())
os->copyBytes(is, palSize * 3); os->copyBytes(is, palSize * 3);
else else
os->copyBytes(is, palSize * cp.pf().bpp/8);
os->copyBytes(is, palSize * server.pf().bpp/8);
break; break;
case tightFilterGradient: case tightFilterGradient:
if (cp.pf().bpp == 8)
if (server.pf().bpp == 8)
throw Exception("TightDecoder: invalid BPP for gradient filter"); throw Exception("TightDecoder: invalid BPP for gradient filter");
break; break;
case tightFilterCopy: case tightFilterCopy:
rowSize = (r.width() + 7) / 8; rowSize = (r.width() + 7) / 8;
else else
rowSize = r.width(); rowSize = r.width();
} else if (cp.pf().is888()) {
} else if (server.pf().is888()) {
rowSize = r.width() * 3; rowSize = r.width() * 3;
} else { } else {
rowSize = r.width() * cp.pf().bpp/8;
rowSize = r.width() * server.pf().bpp/8;
} }


dataSize = r.height() * rowSize; dataSize = r.height() * rowSize;
const Rect& rectB, const Rect& rectB,
const void* bufferB, const void* bufferB,
size_t buflenB, size_t buflenB,
const ConnParams& cp)
const ServerParams& server)
{ {
rdr::U8 comp_ctl_a, comp_ctl_b; rdr::U8 comp_ctl_a, comp_ctl_b;


} }


void TightDecoder::decodeRect(const Rect& r, const void* buffer, void TightDecoder::decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
size_t buflen, const ServerParams& server,
ModifiablePixelBuffer* pb) ModifiablePixelBuffer* pb)
{ {
const rdr::U8* bufptr; const rdr::U8* bufptr;
const PixelFormat& pf = cp.pf();
const PixelFormat& pf = server.pf();


rdr::U8 comp_ctl; rdr::U8 comp_ctl;



+ 3
- 3
common/rfb/TightDecoder.h View File

TightDecoder(); TightDecoder();
virtual ~TightDecoder(); virtual ~TightDecoder();
virtual void readRect(const Rect& r, rdr::InStream* is, virtual void readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os);
const ServerParams& server, rdr::OutStream* os);
virtual bool doRectsConflict(const Rect& rectA, virtual bool doRectsConflict(const Rect& rectA,
const void* bufferA, const void* bufferA,
size_t buflenA, size_t buflenA,
const Rect& rectB, const Rect& rectB,
const void* bufferB, const void* bufferB,
size_t buflenB, size_t buflenB,
const ConnParams& cp);
const ServerParams& server);
virtual void decodeRect(const Rect& r, const void* buffer, virtual void decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
size_t buflen, const ServerParams& server,
ModifiablePixelBuffer* pb); ModifiablePixelBuffer* pb);


private: private:

+ 1
- 2
common/rfb/TightEncoder.cxx View File

#include <rfb/PixelBuffer.h> #include <rfb/PixelBuffer.h>
#include <rfb/Palette.h> #include <rfb/Palette.h>
#include <rfb/encodings.h> #include <rfb/encodings.h>
#include <rfb/ConnParams.h>
#include <rfb/SConnection.h> #include <rfb/SConnection.h>
#include <rfb/TightEncoder.h> #include <rfb/TightEncoder.h>
#include <rfb/TightConstants.h> #include <rfb/TightConstants.h>


bool TightEncoder::isSupported() bool TightEncoder::isSupported()
{ {
return conn->cp.supportsEncoding(encodingTight);
return conn->client.supportsEncoding(encodingTight);
} }


void TightEncoder::setCompressLevel(int level) void TightEncoder::setCompressLevel(int level)

+ 4
- 4
common/rfb/TightJPEGEncoder.cxx View File



bool TightJPEGEncoder::isSupported() bool TightJPEGEncoder::isSupported()
{ {
if (!conn->cp.supportsEncoding(encodingTight))
if (!conn->client.supportsEncoding(encodingTight))
return false; return false;


// Any one of these indicates support for JPEG // Any one of these indicates support for JPEG
if (conn->cp.qualityLevel != -1)
if (conn->client.qualityLevel != -1)
return true; return true;
if (conn->cp.fineQualityLevel != -1)
if (conn->client.fineQualityLevel != -1)
return true; return true;
if (conn->cp.subsampling != -1)
if (conn->client.subsampling != -1)
return true; return true;


// Tight support, but not JPEG // Tight support, but not JPEG

+ 1
- 16
common/rfb/UpdateTracker.cxx View File



// SimpleUpdateTracker // SimpleUpdateTracker


SimpleUpdateTracker::SimpleUpdateTracker(bool use_copyrect) {
copy_enabled = use_copyrect;
SimpleUpdateTracker::SimpleUpdateTracker() {
} }


SimpleUpdateTracker::~SimpleUpdateTracker() { SimpleUpdateTracker::~SimpleUpdateTracker() {
} }


void SimpleUpdateTracker::enable_copyrect(bool enable) {
if (!enable && copy_enabled) {
add_changed(copied);
copied.clear();
}
copy_enabled=enable;
}

void SimpleUpdateTracker::add_changed(const Region &region) { void SimpleUpdateTracker::add_changed(const Region &region) {
changed.assign_union(region); changed.assign_union(region);
} }


void SimpleUpdateTracker::add_copied(const Region &dest, const Point &delta) { void SimpleUpdateTracker::add_copied(const Region &dest, const Point &delta) {
// Do we support copyrect?
if (!copy_enabled) {
add_changed(dest);
return;
}

// Is there anything to do? // Is there anything to do?
if (dest.is_empty()) return; if (dest.is_empty()) return;



+ 1
- 4
common/rfb/UpdateTracker.h View File



class SimpleUpdateTracker : public UpdateTracker { class SimpleUpdateTracker : public UpdateTracker {
public: public:
SimpleUpdateTracker(bool use_copyrect=true);
SimpleUpdateTracker();
virtual ~SimpleUpdateTracker(); virtual ~SimpleUpdateTracker();


virtual void enable_copyrect(bool enable);

virtual void add_changed(const Region &region); virtual void add_changed(const Region &region);
virtual void add_copied(const Region &dest, const Point &delta); virtual void add_copied(const Region &dest, const Point &delta);
virtual void subtract(const Region& region); virtual void subtract(const Region& region);
Region changed; Region changed;
Region copied; Region copied;
Point copy_delta; Point copy_delta;
bool copy_enabled;
}; };


} }

+ 53
- 61
common/rfb/VNCSConnectionST.cxx View File

inProcessMessages(false), inProcessMessages(false),
pendingSyncFence(false), syncFence(false), fenceFlags(0), pendingSyncFence(false), syncFence(false), fenceFlags(0),
fenceDataLen(0), fenceData(NULL), congestionTimer(this), fenceDataLen(0), fenceData(NULL), congestionTimer(this),
losslessTimer(this), server(server_), updates(false),
losslessTimer(this), server(server_),
updateRenderedCursor(false), removeRenderedCursor(false), updateRenderedCursor(false), removeRenderedCursor(false),
continuousUpdates(false), encodeManager(this), idleTimer(this), continuousUpdates(false), encodeManager(this), idleTimer(this),
pointerEventTime(0), clientHasCursor(false) pointerEventTime(0), clientHasCursor(false)
{ {
try { try {
if (!authenticated()) return; if (!authenticated()) return;
if (cp.width && cp.height &&
(server->getPixelBuffer()->width() != cp.width ||
server->getPixelBuffer()->height() != cp.height))
if (client.width() && client.height() &&
(server->getPixelBuffer()->width() != client.width() ||
server->getPixelBuffer()->height() != client.height()))
{ {
// We need to clip the next update to the new size, but also add any // We need to clip the next update to the new size, but also add any
// extra bits if it's bigger. If we wanted to do this exactly, something // extra bits if it's bigger. If we wanted to do this exactly, something


//updates.intersect(server->pb->getRect()); //updates.intersect(server->pb->getRect());
// //
//if (server->pb->width() > cp.width)
// updates.add_changed(Rect(cp.width, 0, server->pb->width(),
//if (server->pb->width() > client.width())
// updates.add_changed(Rect(client.width(), 0, server->pb->width(),
// server->pb->height())); // server->pb->height()));
//if (server->pb->height() > cp.height)
// updates.add_changed(Rect(0, cp.height, cp.width,
//if (server->pb->height() > client.height())
// updates.add_changed(Rect(0, client.height(), client.width(),
// server->pb->height())); // server->pb->height()));


damagedCursorRegion.assign_intersect(server->getPixelBuffer()->getRect()); damagedCursorRegion.assign_intersect(server->getPixelBuffer()->getRect());


cp.width = server->getPixelBuffer()->width();
cp.height = server->getPixelBuffer()->height();
cp.screenLayout = server->getScreenLayout();
client.setDimensions(server->getPixelBuffer()->width(),
server->getPixelBuffer()->height(),
server->getScreenLayout());
if (state() == RFBSTATE_NORMAL) { if (state() == RFBSTATE_NORMAL) {
// We should only send EDS to client asking for both
if (!writer()->writeExtendedDesktopSize()) {
if (!writer()->writeSetDesktopSize()) {
close("Client does not support desktop resize");
return;
}
if (!client.supportsDesktopSize()) {
close("Client does not support desktop resize");
return;
} }
writer()->writeDesktopSize(reasonServer);
} }


// Drop any lossy tracking that is now outside the framebuffer // Drop any lossy tracking that is now outside the framebuffer
// We interpret a low compression level as an indication that the client // We interpret a low compression level as an indication that the client
// wants to prioritise CPU usage over bandwidth, and hence disable the // wants to prioritise CPU usage over bandwidth, and hence disable the
// comparing update tracker. // comparing update tracker.
return (cp.compressLevel == -1) || (cp.compressLevel > 1);
return (client.compressLevel == -1) || (client.compressLevel > 1);
} }




if (state() != RFBSTATE_NORMAL) if (state() != RFBSTATE_NORMAL)
return false; return false;


if (!cp.supportsLocalCursorWithAlpha &&
!cp.supportsLocalCursor && !cp.supportsLocalXCursor)
if (!client.supportsLocalCursor())
return true; return true;
if (!server->getCursorPos().equals(pointerEventPos) && if (!server->getCursorPos().equals(pointerEventPos) &&
(time(0) - pointerEventTime) > 0) (time(0) - pointerEventTime) > 0)
idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); idleTimer.start(secsToMillis(rfb::Server::idleTimeout));


// - Set the connection parameters appropriately // - Set the connection parameters appropriately
cp.width = server->getPixelBuffer()->width();
cp.height = server->getPixelBuffer()->height();
cp.screenLayout = server->getScreenLayout();
cp.setName(server->getName());
cp.setLEDState(server->getLEDState());
client.setDimensions(server->getPixelBuffer()->width(),
server->getPixelBuffer()->height(),
server->getScreenLayout());
client.setName(server->getName());
client.setLEDState(server->getLEDState());
// - Set the default pixel format // - Set the default pixel format
cp.setPF(server->getPixelBuffer()->getPF());
client.setPF(server->getPixelBuffer()->getPF());
char buffer[256]; char buffer[256];
cp.pf().print(buffer, 256);
client.pf().print(buffer, 256);
vlog.info("Server default pixel format %s", buffer); vlog.info("Server default pixel format %s", buffer);


// - Mark the entire display as "dirty" // - Mark the entire display as "dirty"


// Lock key heuristics // Lock key heuristics
// (only for clients that do not support the LED state extension) // (only for clients that do not support the LED state extension)
if (!cp.supportsLEDState) {
if (!client.supportsLEDState()) {
// Always ignore ScrollLock as we don't have a heuristic // Always ignore ScrollLock as we don't have a heuristic
// for that // for that
if (keysym == XK_Scroll_Lock) { if (keysym == XK_Scroll_Lock) {
SConnection::framebufferUpdateRequest(r, incremental); SConnection::framebufferUpdateRequest(r, incremental);


// Check that the client isn't sending crappy requests // Check that the client isn't sending crappy requests
if (!r.enclosed_by(Rect(0, 0, cp.width, cp.height))) {
if (!r.enclosed_by(Rect(0, 0, client.width(), client.height()))) {
vlog.error("FramebufferUpdateRequest %dx%d at %d,%d exceeds framebuffer %dx%d", vlog.error("FramebufferUpdateRequest %dx%d at %d,%d exceeds framebuffer %dx%d",
r.width(), r.height(), r.tl.x, r.tl.y, cp.width, cp.height);
safeRect = r.intersect(Rect(0, 0, cp.width, cp.height));
r.width(), r.height(), r.tl.x, r.tl.y,
client.width(), client.height());
safeRect = r.intersect(Rect(0, 0, client.width(), client.height()));
} else { } else {
safeRect = r; safeRect = r;
} }


// And send the screen layout to the client (which, unlike the // And send the screen layout to the client (which, unlike the
// framebuffer dimensions, the client doesn't get during init) // framebuffer dimensions, the client doesn't get during init)
writer()->writeExtendedDesktopSize();
if (client.supportsEncoding(pseudoEncodingExtendedDesktopSize))
writer()->writeDesktopSize(reasonServer);


// We do not send a DesktopSize since it only contains the // We do not send a DesktopSize since it only contains the
// framebuffer size (which the client already should know) and // framebuffer size (which the client already should know) and
if (!rfb::Server::acceptSetDesktopSize) return; if (!rfb::Server::acceptSetDesktopSize) return;


result = server->setDesktopSize(this, fb_width, fb_height, layout); result = server->setDesktopSize(this, fb_width, fb_height, layout);
writer()->writeExtendedDesktopSize(reasonClient, result,
fb_width, fb_height, layout);
writer()->writeDesktopSize(reasonClient, result);
} }


void VNCSConnectionST::fence(rdr::U32 flags, unsigned len, const char data[]) void VNCSConnectionST::fence(rdr::U32 flags, unsigned len, const char data[])
{ {
Rect rect; Rect rect;


if (!cp.supportsFence || !cp.supportsContinuousUpdates)
if (!client.supportsFence() || !client.supportsContinuousUpdates())
throw Exception("Client tried to enable continuous updates when not allowed"); throw Exception("Client tried to enable continuous updates when not allowed");


continuousUpdates = enable; continuousUpdates = enable;
} }


// supportsLocalCursor() is called whenever the status of // supportsLocalCursor() is called whenever the status of
// cp.supportsLocalCursor has changed. If the client does now support local
// client.supportsLocalCursor() has changed. If the client does now support local
// cursor, we make sure that the old server-side rendered cursor is cleaned up // cursor, we make sure that the old server-side rendered cursor is cleaned up
// and the cursor is sent to the client. // and the cursor is sent to the client.


{ {
// We refuse to use continuous updates if we cannot monitor the buffer // We refuse to use continuous updates if we cannot monitor the buffer
// usage using fences. // usage using fences.
if (!cp.supportsFence)
if (!client.supportsFence())
return; return;


writer()->writeEndOfContinuousUpdates(); writer()->writeEndOfContinuousUpdates();


void VNCSConnectionST::supportsLEDState() void VNCSConnectionST::supportsLEDState()
{ {
if (client.ledState() == ledUnknown)
return;

writer()->writeLEDState(); writer()->writeLEDState();
} }


{ {
char type; char type;


if (!cp.supportsFence)
if (!client.supportsFence())
return; return;


congestion.updatePosition(sock->outStream().length()); congestion.updatePosition(sock->outStream().length());
if (sock->outStream().bufferUsage() > 0) if (sock->outStream().bufferUsage() > 0)
return true; return true;


if (!cp.supportsFence)
if (!client.supportsFence())
return false; return false;


congestion.updatePosition(sock->outStream().length()); congestion.updatePosition(sock->outStream().length());
bool needNewUpdateInfo; bool needNewUpdateInfo;
const RenderedCursor *cursor; const RenderedCursor *cursor;


updates.enable_copyrect(cp.useCopyRect);

// See what the client has requested (if anything) // See what the client has requested (if anything)
if (continuousUpdates) if (continuousUpdates)
req = cuRegion.union_(requested); req = cuRegion.union_(requested);
if (!authenticated()) if (!authenticated())
return; return;


cp.screenLayout = server->getScreenLayout();
client.setDimensions(client.width(), client.height(),
server->getScreenLayout());


if (state() != RFBSTATE_NORMAL) if (state() != RFBSTATE_NORMAL)
return; return;


writer()->writeExtendedDesktopSize(reason, 0, cp.width, cp.height,
cp.screenLayout);
writer()->writeDesktopSize(reason);
} }






// We need to blank out the client's cursor or there will be two // We need to blank out the client's cursor or there will be two
if (needRenderedCursor()) { if (needRenderedCursor()) {
cp.setCursor(emptyCursor);
client.setCursor(emptyCursor);
clientHasCursor = false; clientHasCursor = false;
} else { } else {
cp.setCursor(*server->getCursor());
client.setCursor(*server->getCursor());
clientHasCursor = true; clientHasCursor = true;
} }


if (!writer()->writeSetCursorWithAlpha()) {
if (!writer()->writeSetCursor()) {
if (!writer()->writeSetXCursor()) {
// No client support
return;
}
}
}
if (client.supportsLocalCursor())
writer()->writeCursor();
} }


void VNCSConnectionST::setDesktopName(const char *name) void VNCSConnectionST::setDesktopName(const char *name)
{ {
cp.setName(name);
client.setName(name);


if (state() != RFBSTATE_NORMAL) if (state() != RFBSTATE_NORMAL)
return; return;


if (!writer()->writeSetDesktopName()) {
fprintf(stderr, "Client does not support desktop rename\n");
return;
}
if (client.supportsEncoding(pseudoEncodingDesktopName))
writer()->writeSetDesktopName();
} }


void VNCSConnectionST::setLEDState(unsigned int ledstate) void VNCSConnectionST::setLEDState(unsigned int ledstate)
if (state() != RFBSTATE_NORMAL) if (state() != RFBSTATE_NORMAL)
return; return;


cp.setLEDState(ledstate);
client.setLEDState(ledstate);


writer()->writeLEDState();
if (client.supportsLEDState())
writer()->writeLEDState();
} }


void VNCSConnectionST::setSocketTimeouts() void VNCSConnectionST::setSocketTimeouts()

+ 9
- 6
common/rfb/VNCServerST.cxx View File

delete comparer; delete comparer;
comparer = 0; comparer = 0;


screenLayout = layout;

if (!pb) { if (!pb) {
screenLayout = ScreenSet(); screenLayout = ScreenSet();


return; return;
} }


if (!layout.validate(pb->width(), pb->height()))
throw Exception("setPixelBuffer: invalid screen layout");

screenLayout = layout;

// Assume the framebuffer contents wasn't saved and reset everything // Assume the framebuffer contents wasn't saved and reset everything
// that tracks its contents // that tracks its contents
comparer = new ComparingUpdateTracker(pb); comparer = new ComparingUpdateTracker(pb);
renderedCursorInvalid = true; renderedCursorInvalid = true;
add_changed(pb->getRect()); add_changed(pb->getRect());


// Make sure that we have at least one screen
if (screenLayout.num_screens() == 0)
screenLayout.add_screen(Screen(0, 0, 0, pb->width(), pb->height(), 0));

std::list<VNCSConnectionST*>::iterator ci, ci_next; std::list<VNCSConnectionST*>::iterator ci, ci_next;
for (ci=clients.begin();ci!=clients.end();ci=ci_next) { for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
ci_next = ci; ci_next++; ci_next = ci; ci_next++;
} }
} }


// Make sure that we have at least one screen
if (layout.num_screens() == 0)
layout.add_screen(Screen(0, 0, 0, pb->width(), pb->height(), 0));

setPixelBuffer(pb_, layout); setPixelBuffer(pb_, layout);
} }



+ 4
- 4
common/rfb/ZRLEDecoder.cxx View File

#include <rdr/MemInStream.h> #include <rdr/MemInStream.h>
#include <rdr/OutStream.h> #include <rdr/OutStream.h>


#include <rfb/ConnParams.h>
#include <rfb/ServerParams.h>
#include <rfb/PixelBuffer.h> #include <rfb/PixelBuffer.h>
#include <rfb/ZRLEDecoder.h> #include <rfb/ZRLEDecoder.h>


} }


void ZRLEDecoder::readRect(const Rect& r, rdr::InStream* is, void ZRLEDecoder::readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os)
const ServerParams& server, rdr::OutStream* os)
{ {
rdr::U32 len; rdr::U32 len;


} }


void ZRLEDecoder::decodeRect(const Rect& r, const void* buffer, void ZRLEDecoder::decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
size_t buflen, const ServerParams& server,
ModifiablePixelBuffer* pb) ModifiablePixelBuffer* pb)
{ {
rdr::MemInStream is(buffer, buflen); rdr::MemInStream is(buffer, buflen);
const rfb::PixelFormat& pf = cp.pf();
const rfb::PixelFormat& pf = server.pf();
switch (pf.bpp) { switch (pf.bpp) {
case 8: zrleDecode8 (r, &is, &zis, pf, pb); break; case 8: zrleDecode8 (r, &is, &zis, pf, pb); break;
case 16: zrleDecode16(r, &is, &zis, pf, pb); break; case 16: zrleDecode16(r, &is, &zis, pf, pb); break;

+ 2
- 2
common/rfb/ZRLEDecoder.h View File

ZRLEDecoder(); ZRLEDecoder();
virtual ~ZRLEDecoder(); virtual ~ZRLEDecoder();
virtual void readRect(const Rect& r, rdr::InStream* is, virtual void readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os);
const ServerParams& server, rdr::OutStream* os);
virtual void decodeRect(const Rect& r, const void* buffer, virtual void decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
size_t buflen, const ServerParams& server,
ModifiablePixelBuffer* pb); ModifiablePixelBuffer* pb);
private: private:
rdr::ZlibInStream zis; rdr::ZlibInStream zis;

+ 1
- 2
common/rfb/ZRLEEncoder.cxx View File

#include <rdr/OutStream.h> #include <rdr/OutStream.h>
#include <rfb/Exception.h> #include <rfb/Exception.h>
#include <rfb/encodings.h> #include <rfb/encodings.h>
#include <rfb/ConnParams.h>
#include <rfb/Palette.h> #include <rfb/Palette.h>
#include <rfb/SConnection.h> #include <rfb/SConnection.h>
#include <rfb/ZRLEEncoder.h> #include <rfb/ZRLEEncoder.h>


bool ZRLEEncoder::isSupported() bool ZRLEEncoder::isSupported()
{ {
return conn->cp.supportsEncoding(encodingZRLE);
return conn->client.supportsEncoding(encodingZRLE);
} }


void ZRLEEncoder::writeRect(const PixelBuffer* pb, const Palette& palette) void ZRLEEncoder::writeRect(const PixelBuffer* pb, const Palette& palette)

+ 5
- 5
tests/decperf.cxx View File

CConn(const char *filename); CConn(const char *filename);
~CConn(); ~CConn();


virtual void setDesktopSize(int w, int h);
virtual void initDone();
virtual void setPixelFormat(const rfb::PixelFormat& pf); virtual void setPixelFormat(const rfb::PixelFormat& pf);
virtual void setCursor(int, int, const rfb::Point&, const rdr::U8*); virtual void setCursor(int, int, const rfb::Point&, const rdr::U8*);
virtual void framebufferUpdateStart(); virtual void framebufferUpdateStart();
delete in; delete in;
} }


void CConn::setDesktopSize(int w, int h)
void CConn::initDone()
{ {
CConnection::setDesktopSize(w, h);
setFramebuffer(new rfb::ManagedPixelBuffer(filePF, cp.width, cp.height));
setFramebuffer(new rfb::ManagedPixelBuffer(filePF,
server.width(),
server.height()));
} }


void CConn::setPixelFormat(const rfb::PixelFormat& pf) void CConn::setPixelFormat(const rfb::PixelFormat& pf)

+ 6
- 8
tests/encperf.cxx View File

void getStats(double& ratio, unsigned long long& bytes, void getStats(double& ratio, unsigned long long& bytes,
unsigned long long& rawEquivalent); unsigned long long& rawEquivalent);


virtual void setDesktopSize(int w, int h);
virtual void initDone();
virtual void setCursor(int, int, const rfb::Point&, const rdr::U8*); virtual void setCursor(int, int, const rfb::Point&, const rdr::U8*);
virtual void framebufferUpdateStart(); virtual void framebufferUpdateStart();
virtual void framebufferUpdateEnd(); virtual void framebufferUpdateEnd();
setDesktopSize(width, height); setDesktopSize(width, height);


sc = new SConn(); sc = new SConn();
sc->cp.setPF((bool)translate ? fbPF : pf);
sc->client.setPF((bool)translate ? fbPF : pf);
sc->setEncodings(sizeof(encodings) / sizeof(*encodings), encodings); sc->setEncodings(sizeof(encodings) / sizeof(*encodings), encodings);
} }


sc->getStats(ratio, bytes, rawEquivalent); sc->getStats(ratio, bytes, rawEquivalent);
} }


void CConn::setDesktopSize(int w, int h)
void CConn::initDone()
{ {
rfb::ModifiablePixelBuffer *pb; rfb::ModifiablePixelBuffer *pb;


CConnection::setDesktopSize(w, h);

pb = new rfb::ManagedPixelBuffer((bool)translate ? fbPF : cp.pf(),
cp.width, cp.height);
pb = new rfb::ManagedPixelBuffer((bool)translate ? fbPF : server.pf(),
server.width(), server.height());
setFramebuffer(pb); setFramebuffer(pb);
} }


out = new DummyOutStream; out = new DummyOutStream;
setStreams(NULL, out); setStreams(NULL, out);


setWriter(new rfb::SMsgWriter(&cp, out));
setWriter(new rfb::SMsgWriter(&client, out));


manager = new Manager(this); manager = new Manager(this);
} }

+ 19
- 0
unix/x0vncserver/XDesktop.cxx View File



layout = ::computeScreenLayout(&outputIdMap); layout = ::computeScreenLayout(&outputIdMap);
XRRFreeScreenResources(res); XRRFreeScreenResources(res);

// Adjust the layout relative to the geometry
ScreenSet::iterator iter, iter_next;
Point offset(-geometry->offsetLeft(), -geometry->offsetTop());
for (iter = layout.begin();iter != layout.end();iter = iter_next) {
iter_next = iter; ++iter_next;
iter->dimensions = iter->dimensions.translate(offset);
if (iter->dimensions.enclosed_by(geometry->getRect()))
continue;
iter->dimensions = iter->dimensions.intersect(geometry->getRect());
if (iter->dimensions.is_empty()) {
layout.remove_screen(iter->id);
}
}
#endif #endif


// Make sure that we have at least one screen
if (layout.num_screens() == 0)
layout.add_screen(rfb::Screen(0, 0, 0, geometry->width(),
geometry->height(), 0));

return layout; return layout;
} }



+ 53
- 212
vncviewer/CConn.cxx View File

#include <rfb/screenTypes.h> #include <rfb/screenTypes.h>
#include <rfb/fenceTypes.h> #include <rfb/fenceTypes.h>
#include <rfb/Timer.h> #include <rfb/Timer.h>
#include <rdr/MemInStream.h>
#include <rdr/MemOutStream.h>
#include <network/TcpSocket.h> #include <network/TcpSocket.h>
#ifndef WIN32 #ifndef WIN32
#include <network/UnixSocket.h> #include <network/UnixSocket.h>


CConn::CConn(const char* vncServerName, network::Socket* socket=NULL) CConn::CConn(const char* vncServerName, network::Socket* socket=NULL)
: serverHost(0), serverPort(0), desktop(NULL), : serverHost(0), serverPort(0), desktop(NULL),
updateCount(0), pixelCount(0), pendingPFChange(false),
currentEncoding(encodingTight), lastServerEncoding((unsigned int)-1),
formatChange(false), encodingChange(false),
firstUpdate(true), pendingUpdate(false), continuousUpdates(false),
forceNonincremental(true), supportsSyncFence(false)
updateCount(0), pixelCount(0),
lastServerEncoding((unsigned int)-1)
{ {
setShared(::shared); setShared(::shared);
sock = socket; sock = socket;


int encNum = encodingNum(preferredEncoding);
if (encNum != -1)
currentEncoding = encNum;

cp.supportsLocalCursor = true;

cp.supportsDesktopResize = true;
cp.supportsExtendedDesktopSize = true;
cp.supportsDesktopRename = true;

cp.supportsLEDState = true;
supportsLocalCursor = true;
supportsDesktopResize = true;
supportsLEDState = true;


if (customCompressLevel) if (customCompressLevel)
cp.compressLevel = compressLevel;
else
cp.compressLevel = -1;
setCompressLevel(::compressLevel);


if (!noJpeg) if (!noJpeg)
cp.qualityLevel = qualityLevel;
else
cp.qualityLevel = -1;
setQualityLevel(::qualityLevel);


if(sock == NULL) { if(sock == NULL) {
try { try {
delete sock; delete sock;
} }


void CConn::refreshFramebuffer()
{
forceNonincremental = true;

// Without fences, we cannot safely trigger an update request directly
// but must wait for the next update to arrive.
if (supportsSyncFence)
requestNewUpdate();
}

const char *CConn::connectionInfo() const char *CConn::connectionInfo()
{ {
static char infoText[1024] = ""; static char infoText[1024] = "";
infoText[0] = '\0'; infoText[0] = '\0';


snprintf(scratch, sizeof(scratch), snprintf(scratch, sizeof(scratch),
_("Desktop name: %.80s"), cp.name());
_("Desktop name: %.80s"), server.name());
strcat(infoText, scratch); strcat(infoText, scratch);
strcat(infoText, "\n"); strcat(infoText, "\n");


strcat(infoText, "\n"); strcat(infoText, "\n");


snprintf(scratch, sizeof(scratch), snprintf(scratch, sizeof(scratch),
_("Size: %d x %d"), cp.width, cp.height);
_("Size: %d x %d"), server.width(), server.height());
strcat(infoText, scratch); strcat(infoText, scratch);
strcat(infoText, "\n"); strcat(infoText, "\n");


// TRANSLATORS: Will be filled in with a string describing the // TRANSLATORS: Will be filled in with a string describing the
// protocol pixel format in a fairly language neutral way // protocol pixel format in a fairly language neutral way
cp.pf().print(pfStr, 100);
server.pf().print(pfStr, 100);
snprintf(scratch, sizeof(scratch), snprintf(scratch, sizeof(scratch),
_("Pixel format: %s"), pfStr); _("Pixel format: %s"), pfStr);
strcat(infoText, scratch); strcat(infoText, scratch);
strcat(infoText, "\n"); strcat(infoText, "\n");


snprintf(scratch, sizeof(scratch), snprintf(scratch, sizeof(scratch),
_("Requested encoding: %s"), encodingName(currentEncoding));
_("Requested encoding: %s"), encodingName(getPreferredEncoding()));
strcat(infoText, scratch); strcat(infoText, scratch);
strcat(infoText, "\n"); strcat(infoText, "\n");


strcat(infoText, "\n"); strcat(infoText, "\n");


snprintf(scratch, sizeof(scratch), snprintf(scratch, sizeof(scratch),
_("Protocol version: %d.%d"), cp.majorVersion, cp.minorVersion);
_("Protocol version: %d.%d"), server.majorVersion, server.minorVersion);
strcat(infoText, scratch); strcat(infoText, scratch);
strcat(infoText, "\n"); strcat(infoText, "\n");




////////////////////// CConnection callback methods ////////////////////// ////////////////////// CConnection callback methods //////////////////////


// serverInit() is called when the serverInit message has been received. At
// initDone() is called when the serverInit message has been received. At
// this point we create the desktop window and display it. We also tell the // this point we create the desktop window and display it. We also tell the
// server the pixel format and encodings to use and request the first update. // server the pixel format and encodings to use and request the first update.
void CConn::serverInit()
void CConn::initDone()
{ {
CConnection::serverInit();

// If using AutoSelect with old servers, start in FullColor // If using AutoSelect with old servers, start in FullColor
// mode. See comment in autoSelectFormatAndEncoding. // mode. See comment in autoSelectFormatAndEncoding.
if (cp.beforeVersion(3, 8) && autoSelect)
if (server.beforeVersion(3, 8) && autoSelect)
fullColour.setParam(true); fullColour.setParam(true);


serverPF = cp.pf();
serverPF = server.pf();


desktop = new DesktopWindow(cp.width, cp.height, cp.name(), serverPF, this);
desktop = new DesktopWindow(server.width(), server.height(),
server.name(), serverPF, this);
fullColourPF = desktop->getPreferredPF(); fullColourPF = desktop->getPreferredPF();


// Force a switch to the format and encoding we'd like // Force a switch to the format and encoding we'd like
formatChange = encodingChange = true;

// And kick off the update cycle
requestNewUpdate();

// This initial update request is a bit of a corner case, so we need
// to help out setting the correct format here.
assert(pendingPFChange);
cp.setPF(pendingPF);
pendingPFChange = false;
updatePixelFormat();
int encNum = encodingNum(::preferredEncoding);
if (encNum != -1)
setPreferredEncoding(encNum);
} }


// setDesktopSize() is called when the desktop size changes (including when // setDesktopSize() is called when the desktop size changes (including when
void CConn::setName(const char* name) void CConn::setName(const char* name)
{ {
CConnection::setName(name); CConnection::setName(name);
if (desktop)
desktop->setName(name);
desktop->setName(name);
} }


// framebufferUpdateStart() is called at the beginning of an update. // framebufferUpdateStart() is called at the beginning of an update.
{ {
CConnection::framebufferUpdateStart(); CConnection::framebufferUpdateStart();


// Note: This might not be true if sync fences are supported
pendingUpdate = false;

requestNewUpdate();

// Update the screen prematurely for very slow updates // Update the screen prematurely for very slow updates
Fl::add_timeout(1.0, handleUpdateTimeout, this); Fl::add_timeout(1.0, handleUpdateTimeout, this);
} }
Fl::remove_timeout(handleUpdateTimeout, this); Fl::remove_timeout(handleUpdateTimeout, this);
desktop->updateWindow(); desktop->updateWindow();


if (firstUpdate) {
// We need fences to make extra update requests and continuous
// updates "safe". See fence() for the next step.
if (cp.supportsFence)
writer()->writeFence(fenceFlagRequest | fenceFlagSyncNext, 0, NULL);

firstUpdate = false;
}

// A format change has been scheduled and we are now past the update
// with the old format. Time to active the new one.
if (pendingPFChange) {
cp.setPF(pendingPF);
pendingPFChange = false;
}

// Compute new settings based on updated bandwidth values // Compute new settings based on updated bandwidth values
if (autoSelect) if (autoSelect)
autoSelectFormatAndEncoding(); autoSelectFormatAndEncoding();
writer()->writeFence(flags, len, data); writer()->writeFence(flags, len, data);
return; return;
} }

if (len == 0) {
// Initial probe
if (flags & fenceFlagSyncNext) {
supportsSyncFence = true;

if (cp.supportsContinuousUpdates) {
vlog.info(_("Enabling continuous updates"));
continuousUpdates = true;
writer()->writeEnableContinuousUpdates(true, 0, 0, cp.width, cp.height);
}
}
} else {
// Pixel format change
rdr::MemInStream memStream(data, len);
PixelFormat pf;

pf.read(&memStream);

cp.setPF(pf);
}
} }


void CConn::setLEDState(unsigned int state) void CConn::setLEDState(unsigned int state)


void CConn::resizeFramebuffer() void CConn::resizeFramebuffer()
{ {
if (!desktop)
return;

if (continuousUpdates)
writer()->writeEnableContinuousUpdates(true, 0, 0, cp.width, cp.height);

desktop->resizeFramebuffer(cp.width, cp.height);
desktop->resizeFramebuffer(server.width(), server.height());
} }


// autoSelectFormatAndEncoding() chooses the format and encoding appropriate // autoSelectFormatAndEncoding() chooses the format and encoding appropriate
int kbitsPerSecond = sock->inStream().kbitsPerSecond(); int kbitsPerSecond = sock->inStream().kbitsPerSecond();
unsigned int timeWaited = sock->inStream().timeWaited(); unsigned int timeWaited = sock->inStream().timeWaited();
bool newFullColour = fullColour; bool newFullColour = fullColour;
int newQualityLevel = qualityLevel;
int newQualityLevel = ::qualityLevel;


// Always use Tight // Always use Tight
if (currentEncoding != encodingTight) {
currentEncoding = encodingTight;
encodingChange = true;
}
setPreferredEncoding(encodingTight);


// Check that we have a decent bandwidth measurement // Check that we have a decent bandwidth measurement
if ((kbitsPerSecond == 0) || (timeWaited < 10000)) if ((kbitsPerSecond == 0) || (timeWaited < 10000))
else else
newQualityLevel = 6; newQualityLevel = 6;


if (newQualityLevel != qualityLevel) {
if (newQualityLevel != ::qualityLevel) {
vlog.info(_("Throughput %d kbit/s - changing to quality %d"), vlog.info(_("Throughput %d kbit/s - changing to quality %d"),
kbitsPerSecond, newQualityLevel); kbitsPerSecond, newQualityLevel);
cp.qualityLevel = newQualityLevel;
qualityLevel.setParam(newQualityLevel);
encodingChange = true;
::qualityLevel.setParam(newQualityLevel);
setQualityLevel(newQualityLevel);
} }
} }


if (cp.beforeVersion(3, 8)) {
if (server.beforeVersion(3, 8)) {
// Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with
// cursors "asynchronously". If this happens in the middle of a // cursors "asynchronously". If this happens in the middle of a
// pixel format change, the server will encode the cursor with // pixel format change, the server will encode the cursor with
vlog.info(_("Throughput %d kbit/s - full color is now disabled"), vlog.info(_("Throughput %d kbit/s - full color is now disabled"),
kbitsPerSecond); kbitsPerSecond);
fullColour.setParam(newFullColour); fullColour.setParam(newFullColour);
formatChange = true;
updatePixelFormat();
} }
} }


// checkEncodings() sends a setEncodings message if one is needed.
void CConn::checkEncodings()
{
if (encodingChange && writer()) {
vlog.info(_("Using %s encoding"),encodingName(currentEncoding));
writer()->writeSetEncodings(currentEncoding, true);
encodingChange = false;
}
}

// requestNewUpdate() requests an update from the server, having set the // requestNewUpdate() requests an update from the server, having set the
// format and encoding appropriately. // format and encoding appropriately.
void CConn::requestNewUpdate()
void CConn::updatePixelFormat()
{ {
if (formatChange) {
PixelFormat pf;

/* Catch incorrect requestNewUpdate calls */
assert(!pendingUpdate || supportsSyncFence);

if (fullColour) {
pf = fullColourPF;
} else {
if (lowColourLevel == 0)
pf = verylowColourPF;
else if (lowColourLevel == 1)
pf = lowColourPF;
else
pf = mediumColourPF;
}

if (supportsSyncFence) {
// We let the fence carry the pixel format and switch once we
// get the response back. That way we will be synchronised with
// when the server switches.
rdr::MemOutStream memStream;

pf.write(&memStream);

writer()->writeFence(fenceFlagRequest | fenceFlagSyncNext,
memStream.length(), (const char*)memStream.data());
} else {
// New requests are sent out at the start of processing the last
// one, so we cannot switch our internal format right now (doing so
// would mean misdecoding the current update).
pendingPFChange = true;
pendingPF = pf;
}

char str[256];
pf.print(str, 256);
vlog.info(_("Using pixel format %s"),str);
writer()->writeSetPixelFormat(pf);
PixelFormat pf;


formatChange = false;
if (fullColour) {
pf = fullColourPF;
} else {
if (lowColourLevel == 0)
pf = verylowColourPF;
else if (lowColourLevel == 1)
pf = lowColourPF;
else
pf = mediumColourPF;
} }


checkEncodings();

if (forceNonincremental || !continuousUpdates) {
pendingUpdate = true;
writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
!forceNonincremental);
}
forceNonincremental = false;
char str[256];
pf.print(str, 256);
vlog.info(_("Using pixel format %s"),str);
setPF(pf);
} }


void CConn::handleOptions(void *data) void CConn::handleOptions(void *data)
// list is cheap. Avoid overriding what the auto logic has selected // list is cheap. Avoid overriding what the auto logic has selected
// though. // though.
if (!autoSelect) { if (!autoSelect) {
int encNum = encodingNum(preferredEncoding);
int encNum = encodingNum(::preferredEncoding);


if (encNum != -1) if (encNum != -1)
self->currentEncoding = encNum;
self->setPreferredEncoding(encNum);
} }


self->cp.supportsLocalCursor = true;

if (customCompressLevel) if (customCompressLevel)
self->cp.compressLevel = compressLevel;
self->setCompressLevel(::compressLevel);
else else
self->cp.compressLevel = -1;
self->setCompressLevel(-1);


if (!noJpeg && !autoSelect) if (!noJpeg && !autoSelect)
self->cp.qualityLevel = qualityLevel;
self->setQualityLevel(::qualityLevel);
else else
self->cp.qualityLevel = -1;

self->encodingChange = true;

// Format changes refreshes the entire screen though and are therefore
// very costly. It's probably worth the effort to see if it is necessary
// here.
PixelFormat pf;
self->setQualityLevel(-1);


if (fullColour) {
pf = self->fullColourPF;
} else {
if (lowColourLevel == 0)
pf = verylowColourPF;
else if (lowColourLevel == 1)
pf = lowColourPF;
else
pf = mediumColourPF;
}

if (!pf.equal(self->cp.pf())) {
self->formatChange = true;

// Without fences, we cannot safely trigger an update request directly
// but must wait for the next update to arrive.
if (self->supportsSyncFence)
self->requestNewUpdate();
}
self->updatePixelFormat();
} }


void CConn::handleUpdateTimeout(void *data) void CConn::handleUpdateTimeout(void *data)

+ 3
- 20
vncviewer/CConn.h View File

CConn(const char* vncServerName, network::Socket* sock); CConn(const char* vncServerName, network::Socket* sock);
~CConn(); ~CConn();


void refreshFramebuffer();

const char *connectionInfo(); const char *connectionInfo();


unsigned getUpdateCount(); unsigned getUpdateCount();
static void socketEvent(FL_SOCKET fd, void *data); static void socketEvent(FL_SOCKET fd, void *data);


// CConnection callback methods // CConnection callback methods
void serverInit();
void initDone();


void setDesktopSize(int w, int h); void setDesktopSize(int w, int h);
void setExtendedDesktopSize(unsigned reason, unsigned result, void setExtendedDesktopSize(unsigned reason, unsigned result,
void resizeFramebuffer(); void resizeFramebuffer();


void autoSelectFormatAndEncoding(); void autoSelectFormatAndEncoding();
void checkEncodings();
void requestNewUpdate();
void updatePixelFormat();


static void handleOptions(void *data); static void handleOptions(void *data);


rfb::PixelFormat serverPF; rfb::PixelFormat serverPF;
rfb::PixelFormat fullColourPF; rfb::PixelFormat fullColourPF;


bool pendingPFChange;
rfb::PixelFormat pendingPF;

int currentEncoding, lastServerEncoding;

bool formatChange;
bool encodingChange;

bool firstUpdate;
bool pendingUpdate;
bool continuousUpdates;

bool forceNonincremental;

bool supportsSyncFence;
int lastServerEncoding;
}; };


#endif #endif

+ 14
- 14
vncviewer/DesktopWindow.cxx View File

void DesktopWindow::updateWindow() void DesktopWindow::updateWindow()
{ {
if (firstUpdate) { if (firstUpdate) {
if (cc->cp.supportsSetDesktopSize) {
if (cc->server.supportsSetDesktopSize) {
// Hack: Wait until we're in the proper mode and position until // Hack: Wait until we're in the proper mode and position until
// resizing things, otherwise we might send the wrong thing. // resizing things, otherwise we might send the wrong thing.
if (delayedFullscreen) if (delayedFullscreen)
// d) We're not still waiting for startup fullscreen to kick in // d) We're not still waiting for startup fullscreen to kick in
// //
if (not firstUpdate and not delayedFullscreen and if (not firstUpdate and not delayedFullscreen and
::remoteResize and cc->cp.supportsSetDesktopSize) {
::remoteResize and cc->server.supportsSetDesktopSize) {
// We delay updating the remote desktop as we tend to get a flood // We delay updating the remote desktop as we tend to get a flood
// of resize events as the user is dragging the window. // of resize events as the user is dragging the window.
Fl::remove_timeout(handleResizeTimeout, this); Fl::remove_timeout(handleResizeTimeout, this);
void DesktopWindow::remoteResize(int width, int height) void DesktopWindow::remoteResize(int width, int height)
{ {
ScreenSet layout; ScreenSet layout;
ScreenSet::iterator iter;
ScreenSet::const_iterator iter;


if (!fullscreen_active() || (width > w()) || (height > h())) { if (!fullscreen_active() || (width > w()) || (height > h())) {
// In windowed mode (or the framebuffer is so large that we need // In windowed mode (or the framebuffer is so large that we need
// to scroll) we just report a single virtual screen that covers // to scroll) we just report a single virtual screen that covers
// the entire framebuffer. // the entire framebuffer.


layout = cc->cp.screenLayout;
layout = cc->server.screenLayout();


// Not sure why we have no screens, but adding a new one should be // Not sure why we have no screens, but adding a new one should be
// safe as there is nothing to conflict with... // safe as there is nothing to conflict with...
sy -= viewport_rect.tl.y; sy -= viewport_rect.tl.y;


// Look for perfectly matching existing screen... // Look for perfectly matching existing screen...
for (iter = cc->cp.screenLayout.begin();
iter != cc->cp.screenLayout.end(); ++iter) {
for (iter = cc->server.screenLayout().begin();
iter != cc->server.screenLayout().end(); ++iter) {
if ((iter->dimensions.tl.x == sx) && if ((iter->dimensions.tl.x == sx) &&
(iter->dimensions.tl.y == sy) && (iter->dimensions.tl.y == sy) &&
(iter->dimensions.width() == sw) && (iter->dimensions.width() == sw) &&
} }


// Found it? // Found it?
if (iter != cc->cp.screenLayout.end()) {
if (iter != cc->server.screenLayout().end()) {
layout.add_screen(*iter); layout.add_screen(*iter);
continue; continue;
} }
// Need to add a new one, which means we need to find an unused id // Need to add a new one, which means we need to find an unused id
while (true) { while (true) {
id = rand(); id = rand();
for (iter = cc->cp.screenLayout.begin();
iter != cc->cp.screenLayout.end(); ++iter) {
for (iter = cc->server.screenLayout().begin();
iter != cc->server.screenLayout().end(); ++iter) {
if (iter->id == id) if (iter->id == id)
break; break;
} }


if (iter == cc->cp.screenLayout.end())
if (iter == cc->server.screenLayout().end())
break; break;
} }


} }


// Do we actually change anything? // Do we actually change anything?
if ((width == cc->cp.width) &&
(height == cc->cp.height) &&
(layout == cc->cp.screenLayout))
if ((width == cc->server.width()) &&
(height == cc->server.height()) &&
(layout == cc->server.screenLayout()))
return; return;


char buffer[2048]; char buffer[2048];
vlog.debug("Requesting framebuffer resize from %dx%d to %dx%d", vlog.debug("Requesting framebuffer resize from %dx%d to %dx%d",
cc->cp.width, cc->cp.height, width, height);
cc->server.width(), cc->server.height(), width, height);
layout.print(buffer, sizeof(buffer)); layout.print(buffer, sizeof(buffer));
vlog.debug("%s", buffer); vlog.debug("%s", buffer);



+ 5
- 5
vncviewer/Viewport.cxx View File

unsigned int state; unsigned int state;


// Server support? // Server support?
if (cc->cp.ledState() == ledUnknown)
if (cc->server.ledState() == ledUnknown)
return; return;


state = 0; state = 0;
state |= ledNumLock; state |= ledNumLock;


// No support for Scroll Lock // // No support for Scroll Lock //
state |= (cc->cp.ledState() & ledScrollLock);
state |= (cc->server.ledState() & ledScrollLock);


#else #else
unsigned int mask; unsigned int mask;
state |= ledScrollLock; state |= ledScrollLock;
#endif #endif


if ((state & ledCapsLock) != (cc->cp.ledState() & ledCapsLock)) {
if ((state & ledCapsLock) != (cc->server.ledState() & ledCapsLock)) {
vlog.debug("Inserting fake CapsLock to get in sync with server"); vlog.debug("Inserting fake CapsLock to get in sync with server");
handleKeyPress(0x3a, XK_Caps_Lock); handleKeyPress(0x3a, XK_Caps_Lock);
handleKeyRelease(0x3a); handleKeyRelease(0x3a);
} }
if ((state & ledNumLock) != (cc->cp.ledState() & ledNumLock)) {
if ((state & ledNumLock) != (cc->server.ledState() & ledNumLock)) {
vlog.debug("Inserting fake NumLock to get in sync with server"); vlog.debug("Inserting fake NumLock to get in sync with server");
handleKeyPress(0x45, XK_Num_Lock); handleKeyPress(0x45, XK_Num_Lock);
handleKeyRelease(0x45); handleKeyRelease(0x45);
} }
if ((state & ledScrollLock) != (cc->cp.ledState() & ledScrollLock)) {
if ((state & ledScrollLock) != (cc->server.ledState() & ledScrollLock)) {
vlog.debug("Inserting fake ScrollLock to get in sync with server"); vlog.debug("Inserting fake ScrollLock to get in sync with server");
handleKeyPress(0x46, XK_Scroll_Lock); handleKeyPress(0x46, XK_Scroll_Lock);
handleKeyRelease(0x46); handleKeyRelease(0x46);

Loading…
Cancel
Save