Better to check the actual list of supported encodings directly. Makes parts more readable, and no risk of getting out of sync.tags/v1.9.90
ClientParams::ClientParams() | ClientParams::ClientParams() | ||||
: majorVersion(0), minorVersion(0), | : majorVersion(0), minorVersion(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), | compressLevel(2), qualityLevel(-1), fineQualityLevel(-1), | ||||
subsampling(subsampleUndefined), | subsampling(subsampleUndefined), | ||||
width_(0), height_(0), name_(0), | width_(0), height_(0), name_(0), | ||||
void ClientParams::setEncodings(int nEncodings, const rdr::S32* encodings) | void ClientParams::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; | compressLevel = -1; | ||||
qualityLevel = -1; | qualityLevel = -1; | ||||
fineQualityLevel = -1; | fineQualityLevel = -1; | ||||
for (int i = nEncodings-1; i >= 0; i--) { | for (int i = nEncodings-1; i >= 0; i--) { | ||||
switch (encodings[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: | case pseudoEncodingSubsamp1X: | ||||
subsampling = subsampleNone; | subsampling = subsampleNone; | ||||
break; | break; | ||||
encodings[i] <= pseudoEncodingFineQualityLevel100) | encodings[i] <= pseudoEncodingFineQualityLevel100) | ||||
fineQualityLevel = encodings[i] - pseudoEncodingFineQualityLevel0; | fineQualityLevel = encodings[i] - pseudoEncodingFineQualityLevel0; | ||||
if (encodings[i] > 0) | |||||
encodings_.insert(encodings[i]); | |||||
encodings_.insert(encodings[i]); | |||||
} | } | ||||
} | } | ||||
{ | { | ||||
ledState_ = 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::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; | |||||
} |
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 supportsLEDState() const; | |||||
bool supportsFence() const; | |||||
bool supportsContinuousUpdates() const; | |||||
int compressLevel; | int compressLevel; | ||||
int qualityLevel; | int qualityLevel; |
changed = changed_; | changed = changed_; | ||||
if (!conn->client.useCopyRect) | |||||
if (!conn->client.supportsEncoding(encodingCopyRect)) | |||||
changed.assign_union(copied); | changed.assign_union(copied); | ||||
/* | /* | ||||
changed.assign_subtract(renderedCursor->getEffectiveRect()); | changed.assign_subtract(renderedCursor->getEffectiveRect()); | ||||
} | } | ||||
if (conn->client.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); | ||||
if (conn->client.useCopyRect) | |||||
if (conn->client.supportsEncoding(encodingCopyRect)) | |||||
writeCopyRects(copied, copyDelta); | 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->client.supportsLastRect) | |||||
if (conn->client.supportsEncoding(pseudoEncodingLastRect)) | |||||
writeSolidRects(&changed, pb); | writeSolidRects(&changed, pb); | ||||
writeRects(changed, pb); | writeRects(changed, pb); |
#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; | ||||
bool firstFence, firstContinuousUpdates, firstLEDState, | bool firstFence, firstContinuousUpdates, firstLEDState, | ||||
firstQEMUKeyEvent; | firstQEMUKeyEvent; | ||||
firstFence = !client.supportsFence; | |||||
firstContinuousUpdates = !client.supportsContinuousUpdates; | |||||
firstLEDState = !client.supportsLEDState; | |||||
firstQEMUKeyEvent = !client.supportsQEMUKeyEvent; | |||||
firstFence = !client.supportsFence(); | |||||
firstContinuousUpdates = !client.supportsContinuousUpdates(); | |||||
firstLEDState = !client.supportsLEDState(); | |||||
firstQEMUKeyEvent = !client.supportsEncoding(pseudoEncodingQEMUKeyEvent); | |||||
client.setEncodings(nEncodings, encodings); | client.setEncodings(nEncodings, encodings); | ||||
supportsLocalCursor(); | supportsLocalCursor(); | ||||
if (client.supportsFence && firstFence) | |||||
if (client.supportsFence() && firstFence) | |||||
supportsFence(); | supportsFence(); | ||||
if (client.supportsContinuousUpdates && firstContinuousUpdates) | |||||
if (client.supportsContinuousUpdates() && firstContinuousUpdates) | |||||
supportsContinuousUpdates(); | supportsContinuousUpdates(); | ||||
if (client.supportsLEDState && firstLEDState) | |||||
if (client.supportsLEDState() && firstLEDState) | |||||
supportsLEDState(); | supportsLEDState(); | ||||
if (client.supportsQEMUKeyEvent && firstQEMUKeyEvent) | |||||
if (client.supportsEncoding(pseudoEncodingQEMUKeyEvent) && firstQEMUKeyEvent) | |||||
supportsQEMUKeyEvent(); | supportsQEMUKeyEvent(); | ||||
} | } | ||||
void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[]) | void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[]) | ||||
{ | { | ||||
if (!client->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 (!client->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); | ||||
} | } | ||||
bool SMsgWriter::writeSetDesktopSize() { | bool SMsgWriter::writeSetDesktopSize() { | ||||
if (!client->supportsDesktopResize) | |||||
if (!client->supportsEncoding(pseudoEncodingDesktopSize)) | |||||
return false; | return false; | ||||
needSetDesktopSize = true; | needSetDesktopSize = true; | ||||
} | } | ||||
bool SMsgWriter::writeExtendedDesktopSize() { | bool SMsgWriter::writeExtendedDesktopSize() { | ||||
if (!client->supportsExtendedDesktopSize) | |||||
if (!client->supportsEncoding(pseudoEncodingExtendedDesktopSize)) | |||||
return false; | return false; | ||||
needExtendedDesktopSize = true; | needExtendedDesktopSize = true; | ||||
const ScreenSet& layout) { | const ScreenSet& layout) { | ||||
ExtendedDesktopSizeMsg msg; | ExtendedDesktopSizeMsg msg; | ||||
if (!client->supportsExtendedDesktopSize) | |||||
if (!client->supportsEncoding(pseudoEncodingExtendedDesktopSize)) | |||||
return false; | return false; | ||||
msg.reason = reason; | msg.reason = reason; | ||||
} | } | ||||
bool SMsgWriter::writeSetDesktopName() { | bool SMsgWriter::writeSetDesktopName() { | ||||
if (!client->supportsDesktopRename) | |||||
if (!client->supportsEncoding(pseudoEncodingDesktopName)) | |||||
return false; | return false; | ||||
needSetDesktopName = true; | needSetDesktopName = true; | ||||
bool SMsgWriter::writeSetCursor() | bool SMsgWriter::writeSetCursor() | ||||
{ | { | ||||
if (!client->supportsLocalCursor) | |||||
if (!client->supportsEncoding(pseudoEncodingCursor)) | |||||
return false; | return false; | ||||
needSetCursor = true; | needSetCursor = true; | ||||
bool SMsgWriter::writeSetXCursor() | bool SMsgWriter::writeSetXCursor() | ||||
{ | { | ||||
if (!client->supportsLocalXCursor) | |||||
if (!client->supportsEncoding(pseudoEncodingXCursor)) | |||||
return false; | return false; | ||||
needSetXCursor = true; | needSetXCursor = true; | ||||
bool SMsgWriter::writeSetCursorWithAlpha() | bool SMsgWriter::writeSetCursorWithAlpha() | ||||
{ | { | ||||
if (!client->supportsLocalCursorWithAlpha) | |||||
if (!client->supportsEncoding(pseudoEncodingCursorWithAlpha)) | |||||
return false; | return false; | ||||
needSetCursorWithAlpha = true; | needSetCursorWithAlpha = true; | ||||
bool SMsgWriter::writeLEDState() | bool SMsgWriter::writeLEDState() | ||||
{ | { | ||||
if (!client->supportsLEDState) | |||||
if (!client->supportsEncoding(pseudoEncodingLEDState)) | |||||
return false; | return false; | ||||
if (client->ledState() == ledUnknown) | if (client->ledState() == ledUnknown) | ||||
return false; | return false; | ||||
bool SMsgWriter::writeQEMUKeyEvent() | bool SMsgWriter::writeQEMUKeyEvent() | ||||
{ | { | ||||
if (!client->supportsQEMUKeyEvent) | |||||
if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent)) | |||||
return false; | return false; | ||||
needQEMUKeyEvent = true; | needQEMUKeyEvent = true; | ||||
void SMsgWriter::writeSetDesktopSizeRect(int width, int height) | void SMsgWriter::writeSetDesktopSizeRect(int width, int height) | ||||
{ | { | ||||
if (!client->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 (!client->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 (!client->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 (!client->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"); | ||||
int hotspotX, int hotspotY, | int hotspotX, int hotspotY, | ||||
const void* data, const void* mask) | const void* data, const void* mask) | ||||
{ | { | ||||
if (!client->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 (!client->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 (!client->supportsLEDState) | |||||
if (!client->supportsEncoding(pseudoEncodingLEDState)) | |||||
throw Exception("Client does not support LED state updates"); | throw Exception("Client does not support LED state updates"); | ||||
if (client->ledState() == ledUnknown) | if (client->ledState() == ledUnknown) | ||||
throw Exception("Server does not support LED state updates"); | throw Exception("Server does not support LED state updates"); | ||||
void SMsgWriter::writeQEMUKeyEventRect() | void SMsgWriter::writeQEMUKeyEventRect() | ||||
{ | { | ||||
if (!client->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"); |
if (state() != RFBSTATE_NORMAL) | if (state() != RFBSTATE_NORMAL) | ||||
return false; | return false; | ||||
if (!client.supportsLocalCursorWithAlpha && | |||||
!client.supportsLocalCursor && !client.supportsLocalXCursor) | |||||
if (!client.supportsLocalCursor()) | |||||
return true; | return true; | ||||
if (!server->cursorPos.equals(pointerEventPos) && | if (!server->cursorPos.equals(pointerEventPos) && | ||||
(time(0) - pointerEventTime) > 0) | (time(0) - pointerEventTime) > 0) | ||||
// 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 (!client.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) { | ||||
{ | { | ||||
Rect rect; | Rect rect; | ||||
if (!client.supportsFence || !client.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 | ||||
// client.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 (!client.supportsFence) | |||||
if (!client.supportsFence()) | |||||
return; | return; | ||||
writer()->writeEndOfContinuousUpdates(); | writer()->writeEndOfContinuousUpdates(); | ||||
{ | { | ||||
char type; | char type; | ||||
if (!client.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 (!client.supportsFence) | |||||
if (!client.supportsFence()) | |||||
return false; | return false; | ||||
congestion.updatePosition(sock->outStream().length()); | congestion.updatePosition(sock->outStream().length()); |