@@ -16,6 +16,7 @@ | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
#include <assert.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
@@ -24,6 +25,7 @@ | |||
#include <rfb/CMsgReader.h> | |||
#include <rfb/CMsgWriter.h> | |||
#include <rfb/CSecurity.h> | |||
#include <rfb/Decoder.h> | |||
#include <rfb/Security.h> | |||
#include <rfb/SecurityClient.h> | |||
#include <rfb/CConnection.h> | |||
@@ -39,9 +41,17 @@ using namespace rfb; | |||
static LogWriter vlog("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), | |||
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) | |||
{ | |||
} | |||
@@ -67,6 +77,11 @@ void CConnection::setFramebuffer(ModifiablePixelBuffer* fb) | |||
{ | |||
decoder.flush(); | |||
if (fb) { | |||
assert(fb->width() == server.width()); | |||
assert(fb->height() == server.height()); | |||
} | |||
if ((framebuffer != NULL) && (fb != NULL)) { | |||
Rect rect; | |||
@@ -128,35 +143,51 @@ void CConnection::processMsg() | |||
void CConnection::processVersionMsg() | |||
{ | |||
char verStr[13]; | |||
int majorVersion; | |||
int minorVersion; | |||
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; | |||
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", | |||
cp.majorVersion, cp.minorVersion); | |||
server.majorVersion, server.minorVersion); | |||
// 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", | |||
cp.majorVersion, cp.minorVersion); | |||
server.majorVersion, server.minorVersion); | |||
state_ = RFBSTATE_INVALID; | |||
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; | |||
vlog.info("Using RFB protocol version %d.%d", | |||
cp.majorVersion, cp.minorVersion); | |||
server.majorVersion, server.minorVersion); | |||
} | |||
@@ -169,7 +200,7 @@ void CConnection::processSecurityTypesMsg() | |||
std::list<rdr::U8> secTypes; | |||
secTypes = security.GetEnabledSecTypes(); | |||
if (cp.isVersion(3,3)) { | |||
if (server.isVersion(3,3)) { | |||
// legacy 3.3 server may only offer "vnc authentication" or "none" | |||
@@ -252,7 +283,7 @@ void CConnection::processSecurityResultMsg() | |||
{ | |||
vlog.debug("processing security result message"); | |||
int result; | |||
if (cp.beforeVersion(3,8) && csecurity->getType() == secTypeNone) { | |||
if (server.beforeVersion(3,8) && csecurity->getType() == secTypeNone) { | |||
result = secResultOK; | |||
} else { | |||
if (!is->checkNoWait(1)) return; | |||
@@ -272,7 +303,7 @@ void CConnection::processSecurityResultMsg() | |||
throw Exception("Unknown security result from server"); | |||
} | |||
state_ = RFBSTATE_INVALID; | |||
if (cp.beforeVersion(3,8)) | |||
if (server.beforeVersion(3,8)) | |||
throw AuthFailureException(); | |||
CharArray reason(is->readString()); | |||
throw AuthFailureException(reason.buf); | |||
@@ -296,7 +327,7 @@ void CConnection::securityCompleted() | |||
{ | |||
state_ = RFBSTATE_INITIALISATION; | |||
reader_ = new CMsgReader(this, is); | |||
writer_ = new CMsgWriter(&cp, os); | |||
writer_ = new CMsgWriter(&server, os); | |||
vlog.debug("Authentication success!"); | |||
authSuccess(); | |||
writer_->writeClientInit(shared); | |||
@@ -307,6 +338,16 @@ void CConnection::setDesktopSize(int w, int h) | |||
decoder.flush(); | |||
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, | |||
@@ -317,6 +358,59 @@ void CConnection::setExtendedDesktopSize(unsigned reason, | |||
decoder.flush(); | |||
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, | |||
@@ -329,6 +423,13 @@ void CConnection::readAndDecodeRect(const Rect& r, int encoding, | |||
void CConnection::framebufferUpdateStart() | |||
{ | |||
CMsgHandler::framebufferUpdateStart(); | |||
assert(framebuffer != NULL); | |||
// Note: This might not be true if continuous updates are supported | |||
pendingUpdate = false; | |||
requestNewUpdate(); | |||
} | |||
void CConnection::framebufferUpdateEnd() | |||
@@ -336,6 +437,25 @@ void CConnection::framebufferUpdateEnd() | |||
decoder.flush(); | |||
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) | |||
@@ -347,10 +467,64 @@ void CConnection::authSuccess() | |||
{ | |||
} | |||
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[]) | |||
@@ -365,3 +539,94 @@ void CConnection::fence(rdr::U32 flags, unsigned len, const char 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); | |||
} |
@@ -100,6 +100,12 @@ namespace rfb { | |||
int w, int h, | |||
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, | |||
ModifiablePixelBuffer* pb); | |||
@@ -110,20 +116,43 @@ namespace rfb { | |||
// 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. | |||
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 | |||
// 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_; } | |||
CMsgWriter* writer() { return writer_; } | |||
@@ -159,6 +188,13 @@ namespace rfb { | |||
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: | |||
// This is a default implementation of fences that automatically | |||
// responds to requests, stating no support for synchronisation. | |||
@@ -176,6 +212,9 @@ namespace rfb { | |||
void throwConnFailedException(); | |||
void securityCompleted(); | |||
void requestNewUpdate(); | |||
void updateEncodings(); | |||
rdr::InStream* is; | |||
rdr::OutStream* os; | |||
CMsgReader* reader_; | |||
@@ -188,6 +227,23 @@ namespace rfb { | |||
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; | |||
DecodeManager decoder; | |||
}; |
@@ -11,9 +11,9 @@ set(RFB_SOURCES | |||
CSecurityStack.cxx | |||
CSecurityVeNCrypt.cxx | |||
CSecurityVncAuth.cxx | |||
ClientParams.cxx | |||
ComparingUpdateTracker.cxx | |||
Configuration.cxx | |||
ConnParams.cxx | |||
CopyRectDecoder.cxx | |||
Cursor.cxx | |||
DecodeManager.cxx | |||
@@ -43,6 +43,7 @@ set(RFB_SOURCES | |||
SMsgReader.cxx | |||
SMsgWriter.cxx | |||
ServerCore.cxx | |||
ServerParams.cxx | |||
Security.cxx | |||
SecurityServer.cxx | |||
SecurityClient.cxx |
@@ -34,50 +34,53 @@ CMsgHandler::~CMsgHandler() | |||
void CMsgHandler::setDesktopSize(int width, int height) | |||
{ | |||
cp.width = width; | |||
cp.height = height; | |||
server.setDimensions(width, height); | |||
} | |||
void CMsgHandler::setExtendedDesktopSize(unsigned reason, unsigned result, | |||
int width, int height, | |||
const ScreenSet& layout) | |||
{ | |||
cp.supportsSetDesktopSize = true; | |||
server.supportsSetDesktopSize = true; | |||
if ((reason == reasonClient) && (result != resultSuccess)) | |||
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) | |||
{ | |||
cp.setPF(pf); | |||
server.setPF(pf); | |||
} | |||
void CMsgHandler::setName(const char* name) | |||
{ | |||
cp.setName(name); | |||
server.setName(name); | |||
} | |||
void CMsgHandler::fence(rdr::U32 flags, unsigned len, const char data[]) | |||
{ | |||
cp.supportsFence = true; | |||
server.supportsFence = true; | |||
} | |||
void CMsgHandler::endOfContinuousUpdates() | |||
{ | |||
cp.supportsContinuousUpdates = true; | |||
server.supportsContinuousUpdates = true; | |||
} | |||
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() | |||
@@ -90,5 +93,5 @@ void CMsgHandler::framebufferUpdateEnd() | |||
void CMsgHandler::setLEDState(unsigned int state) | |||
{ | |||
cp.setLEDState(state); | |||
server.setLEDState(state); | |||
} |
@@ -26,7 +26,7 @@ | |||
#include <rdr/types.h> | |||
#include <rfb/Pixel.h> | |||
#include <rfb/ConnParams.h> | |||
#include <rfb/ServerParams.h> | |||
#include <rfb/Rect.h> | |||
#include <rfb/ScreenSet.h> | |||
@@ -41,9 +41,9 @@ namespace rfb { | |||
// The following methods are called as corresponding messages are read. A | |||
// 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 setExtendedDesktopSize(unsigned reason, unsigned result, | |||
@@ -56,7 +56,9 @@ namespace rfb { | |||
virtual void fence(rdr::U32 flags, unsigned len, const char data[]); | |||
virtual void endOfContinuousUpdates(); | |||
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, | |||
ModifiablePixelBuffer* pb) = 0; | |||
@@ -72,7 +74,7 @@ namespace rfb { | |||
virtual void setLEDState(unsigned int state); | |||
ConnParams cp; | |||
ServerParams server; | |||
}; | |||
} | |||
#endif |
@@ -43,13 +43,10 @@ void CMsgReader::readServerInit() | |||
{ | |||
int width = is->readU16(); | |||
int height = is->readU16(); | |||
handler->setDesktopSize(width, height); | |||
PixelFormat pf; | |||
pf.read(is); | |||
handler->setPixelFormat(pf); | |||
CharArray name(is->readString()); | |||
handler->setName(name.buf); | |||
handler->serverInit(); | |||
handler->serverInit(width, height, pf, name.buf); | |||
} | |||
void CMsgReader::readMsg() | |||
@@ -192,10 +189,11 @@ void CMsgReader::readFramebufferUpdate() | |||
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", | |||
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"); | |||
} | |||
@@ -269,7 +267,7 @@ void CMsgReader::readSetCursor(int width, int height, const Point& hotspot) | |||
if (width > maxCursorSize || height > maxCursorSize) | |||
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; | |||
rdr::U8Array data(data_len); | |||
rdr::U8Array mask(mask_len); | |||
@@ -290,14 +288,14 @@ void CMsgReader::readSetCursor(int width, int height, const Point& hotspot) | |||
int byte = y * maskBytesPerRow + 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)) | |||
out[3] = 255; | |||
else | |||
out[3] = 0; | |||
in += handler->cp.pf().bpp/8; | |||
in += handler->server.pf().bpp/8; | |||
out += 4; | |||
} | |||
} | |||
@@ -321,10 +319,10 @@ void CMsgReader::readSetCursorWithAlpha(int width, int height, const Point& hots | |||
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->cp.setPF(origPF); | |||
handler->server.setPF(origPF); | |||
// On-wire data has pre-multiplied alpha, but we store it | |||
// non-pre-multiplied |
@@ -20,19 +20,17 @@ | |||
#include <rdr/OutStream.h> | |||
#include <rfb/msgTypes.h> | |||
#include <rfb/fenceTypes.h> | |||
#include <rfb/encodings.h> | |||
#include <rfb/qemuTypes.h> | |||
#include <rfb/Exception.h> | |||
#include <rfb/PixelFormat.h> | |||
#include <rfb/Rect.h> | |||
#include <rfb/ConnParams.h> | |||
#include <rfb/Decoder.h> | |||
#include <rfb/ServerParams.h> | |||
#include <rfb/CMsgWriter.h> | |||
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_) | |||
{ | |||
} | |||
@@ -54,96 +52,21 @@ void CMsgWriter::writeSetPixelFormat(const PixelFormat& pf) | |||
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); | |||
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(); | |||
} | |||
// 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, | |||
const ScreenSet& layout) | |||
{ | |||
if (!cp->supportsSetDesktopSize) | |||
if (!server->supportsSetDesktopSize) | |||
throw Exception("Server does not support SetDesktopSize"); | |||
startMsg(msgTypeSetDesktopSize); | |||
@@ -182,7 +105,7 @@ void CMsgWriter::writeFramebufferUpdateRequest(const Rect& r, bool incremental) | |||
void CMsgWriter::writeEnableContinuousUpdates(bool enable, | |||
int x, int y, int w, int h) | |||
{ | |||
if (!cp->supportsContinuousUpdates) | |||
if (!server->supportsContinuousUpdates) | |||
throw Exception("Server does not support continuous updates"); | |||
startMsg(msgTypeEnableContinuousUpdates); | |||
@@ -199,7 +122,7 @@ void CMsgWriter::writeEnableContinuousUpdates(bool enable, | |||
void CMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[]) | |||
{ | |||
if (!cp->supportsFence) | |||
if (!server->supportsFence) | |||
throw Exception("Server does not support fences"); | |||
if (len > 64) | |||
throw Exception("Too large fence payload"); | |||
@@ -219,7 +142,7 @@ void CMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[]) | |||
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 */ | |||
if (!keysym) | |||
return; | |||
@@ -245,8 +168,8 @@ void CMsgWriter::writePointerEvent(const Point& pos, int buttonMask) | |||
Point p(pos); | |||
if (p.x < 0) p.x = 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); | |||
os->writeU8(buttonMask); |
@@ -23,6 +23,8 @@ | |||
#ifndef __RFB_CMSGWRITER_H__ | |||
#define __RFB_CMSGWRITER_H__ | |||
#include <list> | |||
#include <rdr/types.h> | |||
namespace rdr { class OutStream; } | |||
@@ -30,21 +32,20 @@ namespace rdr { class OutStream; } | |||
namespace rfb { | |||
class PixelFormat; | |||
class ConnParams; | |||
class ServerParams; | |||
struct ScreenSet; | |||
struct Point; | |||
struct Rect; | |||
class CMsgWriter { | |||
public: | |||
CMsgWriter(ConnParams* cp, rdr::OutStream* os); | |||
CMsgWriter(ServerParams* server, rdr::OutStream* os); | |||
virtual ~CMsgWriter(); | |||
void writeClientInit(bool shared); | |||
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 writeFramebufferUpdateRequest(const Rect& r,bool incremental); | |||
@@ -60,7 +61,7 @@ namespace rfb { | |||
void startMsg(int type); | |||
void endMsg(); | |||
ConnParams* cp; | |||
ServerParams* server; | |||
rdr::OutStream* os; | |||
}; | |||
} |
@@ -34,7 +34,6 @@ | |||
#endif | |||
#include <rfb/CSecurityTLS.h> | |||
#include <rfb/SSecurityVeNCrypt.h> | |||
#include <rfb/CConnection.h> | |||
#include <rfb/LogWriter.h> | |||
#include <rfb/Exception.h> |
@@ -31,7 +31,6 @@ | |||
#endif | |||
#include <rfb/CSecurity.h> | |||
#include <rfb/SSecurityVeNCrypt.h> | |||
#include <rfb/Security.h> | |||
#include <rfb/UserMsgBox.h> | |||
#include <rdr/InStream.h> |
@@ -0,0 +1,178 @@ | |||
/* 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; | |||
} |
@@ -1,5 +1,5 @@ | |||
/* 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 | |||
* it under the terms of the GNU General Public License as published by | |||
@@ -17,11 +17,11 @@ | |||
* 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> | |||
@@ -30,8 +30,6 @@ | |||
#include <rfb/PixelFormat.h> | |||
#include <rfb/ScreenSet.h> | |||
namespace rdr { class InStream; } | |||
namespace rfb { | |||
const int subsampleUndefined = -1; | |||
@@ -42,13 +40,10 @@ namespace rfb { | |||
const int subsample8X = 4; | |||
const int subsample16X = 5; | |||
class ConnParams { | |||
class ClientParams { | |||
public: | |||
ConnParams(); | |||
~ConnParams(); | |||
bool readVersion(rdr::InStream* is, bool* done); | |||
void writeVersion(rdr::OutStream* os); | |||
ClientParams(); | |||
~ClientParams(); | |||
int majorVersion; | |||
int minorVersion; | |||
@@ -67,9 +62,11 @@ namespace rfb { | |||
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_; } | |||
void setPF(const PixelFormat& pf); | |||
@@ -87,21 +84,13 @@ namespace rfb { | |||
unsigned int ledState() { return ledState_; } | |||
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 qualityLevel; | |||
@@ -110,12 +99,14 @@ namespace rfb { | |||
private: | |||
int width_; | |||
int height_; | |||
ScreenSet screenLayout_; | |||
PixelFormat pf_; | |||
char* name_; | |||
Cursor* cursor_; | |||
std::set<rdr::S32> encodings_; | |||
char verStr[13]; | |||
int verStrPos; | |||
unsigned int ledState_; | |||
}; | |||
} |
@@ -1,200 +0,0 @@ | |||
/* 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; | |||
} |
@@ -32,7 +32,7 @@ CopyRectDecoder::~CopyRectDecoder() | |||
} | |||
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); | |||
} | |||
@@ -41,21 +41,21 @@ void CopyRectDecoder::readRect(const Rect& r, rdr::InStream* is, | |||
void CopyRectDecoder::getAffectedRegion(const Rect& rect, | |||
const void* buffer, | |||
size_t buflen, | |||
const ConnParams& cp, | |||
const ServerParams& server, | |||
Region* region) | |||
{ | |||
rdr::MemInStream is(buffer, buflen); | |||
int srcX = 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, | |||
srcY-rect.tl.y)))); | |||
} | |||
void CopyRectDecoder::decodeRect(const Rect& r, const void* buffer, | |||
size_t buflen, const ConnParams& cp, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb) | |||
{ | |||
rdr::MemInStream is(buffer, buflen); |
@@ -27,12 +27,12 @@ namespace rfb { | |||
CopyRectDecoder(); | |||
virtual ~CopyRectDecoder(); | |||
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, | |||
size_t buflen, const ConnParams& cp, | |||
size_t buflen, const ServerParams& server, | |||
Region* region); | |||
virtual void decodeRect(const Rect& r, const void* buffer, | |||
size_t buflen, const ConnParams& cp, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb); | |||
}; | |||
} |
@@ -132,9 +132,9 @@ void DecodeManager::decodeRect(const Rect& r, int encoding, | |||
if (threads.empty()) { | |||
bufferStream = freeBuffers.front(); | |||
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(), | |||
conn->cp, pb); | |||
conn->server, pb); | |||
return; | |||
} | |||
@@ -155,7 +155,7 @@ void DecodeManager::decodeRect(const Rect& r, int encoding, | |||
// Read the rect | |||
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 | |||
entry = new QueueEntry; | |||
@@ -164,12 +164,12 @@ void DecodeManager::decodeRect(const Rect& r, int encoding, | |||
entry->rect = r; | |||
entry->encoding = encoding; | |||
entry->decoder = decoder; | |||
entry->cp = &conn->cp; | |||
entry->server = &conn->server; | |||
entry->pb = pb; | |||
entry->bufferStream = bufferStream; | |||
decoder->getAffectedRegion(r, bufferStream->data(), | |||
bufferStream->length(), conn->cp, | |||
bufferStream->length(), conn->server, | |||
&entry->affectedRegion); | |||
queueMutex->lock(); | |||
@@ -276,7 +276,7 @@ void DecodeManager::DecodeThread::worker() | |||
try { | |||
entry->decoder->decodeRect(entry->rect, entry->bufferStream->data(), | |||
entry->bufferStream->length(), | |||
*entry->cp, entry->pb); | |||
*entry->server, entry->pb); | |||
} catch (rdr::Exception& e) { | |||
manager->setThreadException(e); | |||
} catch(...) { | |||
@@ -346,7 +346,7 @@ DecodeManager::QueueEntry* DecodeManager::DecodeThread::findEntry() | |||
(*iter2)->rect, | |||
(*iter2)->bufferStream->data(), | |||
(*iter2)->bufferStream->length(), | |||
*entry->cp)) | |||
*entry->server)) | |||
goto next; | |||
} | |||
} |
@@ -65,7 +65,7 @@ namespace rfb { | |||
Rect rect; | |||
int encoding; | |||
Decoder* decoder; | |||
const ConnParams* cp; | |||
const ServerParams* server; | |||
ModifiablePixelBuffer* pb; | |||
rdr::MemOutStream* bufferStream; | |||
Region affectedRegion; |
@@ -38,7 +38,7 @@ Decoder::~Decoder() | |||
} | |||
void Decoder::getAffectedRegion(const Rect& rect, const void* buffer, | |||
size_t buflen, const ConnParams& cp, | |||
size_t buflen, const ServerParams& server, | |||
Region* region) | |||
{ | |||
region->reset(rect); | |||
@@ -47,7 +47,7 @@ void Decoder::getAffectedRegion(const Rect& rect, const void* buffer, | |||
bool Decoder::doRectsConflict(const Rect& rectA, const void* bufferA, | |||
size_t buflenA, const Rect& rectB, | |||
const void* bufferB, size_t buflenB, | |||
const ConnParams& cp) | |||
const ServerParams& server) | |||
{ | |||
return false; | |||
} |
@@ -25,7 +25,7 @@ namespace rdr { | |||
} | |||
namespace rfb { | |||
class ConnParams; | |||
class ServerParams; | |||
class ModifiablePixelBuffer; | |||
class Region; | |||
@@ -53,7 +53,7 @@ namespace rfb { | |||
// make it easier to decode. This function will always be called in | |||
// a serial manner on the main thread. | |||
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. | |||
// A lock will be held whilst these are called so it is safe to | |||
@@ -63,7 +63,7 @@ namespace rfb { | |||
// be either read from or written do when decoding this rect. The | |||
// default implementation simply returns the given rectangle. | |||
virtual void getAffectedRegion(const Rect& rect, const void* buffer, | |||
size_t buflen, const ConnParams& cp, | |||
size_t buflen, const ServerParams& server, | |||
Region* region); | |||
// doesRectsConflict() determines if two rectangles must be decoded | |||
@@ -75,14 +75,14 @@ namespace rfb { | |||
const Rect& rectB, | |||
const void* bufferB, | |||
size_t buflenB, | |||
const ConnParams& cp); | |||
const ServerParams& server); | |||
// decodeRect() decodes the given rectangle with data from the | |||
// given buffer, onto the ModifiablePixelBuffer. The PixelFormat of | |||
// the PixelBuffer might not match the ConnParams and it is up to | |||
// the decoder to do any necessary conversion. | |||
virtual void decodeRect(const Rect& r, const void* buffer, | |||
size_t buflen, const ConnParams& cp, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb)=0; | |||
public: |
@@ -325,6 +325,9 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_, | |||
changed = changed_; | |||
if (!conn->client.supportsEncoding(encodingCopyRect)) | |||
changed.assign_union(copied); | |||
/* | |||
* We need to render the cursor seperately as it has its own | |||
* magical pixel buffer, so split it out from the changed region. | |||
@@ -334,7 +337,7 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_, | |||
changed.assign_subtract(renderedCursor->getEffectiveRect()); | |||
} | |||
if (conn->cp.supportsLastRect) | |||
if (conn->client.supportsEncoding(pseudoEncodingLastRect)) | |||
nRects = 0xFFFF; | |||
else { | |||
nRects = copied.numRects(); | |||
@@ -344,13 +347,14 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_, | |||
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 | |||
* from the changed region. | |||
*/ | |||
if (conn->cp.supportsLastRect) | |||
if (conn->client.supportsEncoding(pseudoEncodingLastRect)) | |||
writeSolidRects(&changed, pb); | |||
writeRects(changed, pb); | |||
@@ -373,7 +377,7 @@ void EncodeManager::prepareEncoders(bool allowLossy) | |||
solid = bitmap = bitmapRLE = encoderRaw; | |||
indexed = indexedRLE = fullColour = encoderRaw; | |||
allowJPEG = conn->cp.pf().bpp >= 16; | |||
allowJPEG = conn->client.pf().bpp >= 16; | |||
if (!allowLossy) { | |||
if (encoders[encoderTightJPEG]->losslessQuality == -1) | |||
allowJPEG = false; | |||
@@ -447,7 +451,7 @@ void EncodeManager::prepareEncoders(bool allowLossy) | |||
} | |||
// 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) { | |||
solid = bitmap = bitmapRLE = encoderTightJPEG; | |||
indexed = indexedRLE = fullColour = encoderTightJPEG; | |||
@@ -465,14 +469,14 @@ void EncodeManager::prepareEncoders(bool allowLossy) | |||
encoder = encoders[*iter]; | |||
encoder->setCompressLevel(conn->cp.compressLevel); | |||
encoder->setCompressLevel(conn->client.compressLevel); | |||
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 { | |||
int level = __rfbmax(conn->cp.qualityLevel, | |||
int level = __rfbmax(conn->client.qualityLevel, | |||
encoder->losslessQuality); | |||
encoder->setQualityLevel(level); | |||
encoder->setFineQualityLevel(-1, subsampleUndefined); | |||
@@ -575,7 +579,7 @@ Encoder *EncodeManager::startRect(const Rect& rect, int type) | |||
stats[klass][activeType].rects++; | |||
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; | |||
encoder = encoders[klass]; | |||
@@ -623,7 +627,7 @@ void EncodeManager::writeCopyRects(const Region& copied, const Point& delta) | |||
copyStats.rects++; | |||
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; | |||
conn->writer()->writeCopyRect(*rect, rect->tl.x - delta.x, | |||
@@ -710,11 +714,11 @@ void EncodeManager::findSolidRect(const Rect& rect, Region *changed, | |||
rdr::U32 _buffer2; | |||
rdr::U8* converted = (rdr::U8*)&_buffer2; | |||
conn->cp.pf().bufferFromBuffer(converted, pb->getPF(), | |||
conn->client.pf().bufferFromBuffer(converted, pb->getPF(), | |||
colourValue, 1); | |||
encoder->writeSolidRect(erp.width(), erp.height(), | |||
conn->cp.pf(), converted); | |||
conn->client.pf(), converted); | |||
} | |||
endRect(); | |||
@@ -808,10 +812,10 @@ void EncodeManager::writeSubRect(const Rect& rect, const PixelBuffer *pb) | |||
// compression setting means spending less effort in building | |||
// a palette. It might be that they figured the increase in | |||
// zlib setting compensated for the loss. | |||
if (conn->cp.compressLevel == -1) | |||
if (conn->client.compressLevel == -1) | |||
divisor = 2 * 8; | |||
else | |||
divisor = conn->cp.compressLevel * 8; | |||
divisor = conn->client.compressLevel * 8; | |||
if (divisor < 4) | |||
divisor = 4; | |||
@@ -819,7 +823,7 @@ void EncodeManager::writeSubRect(const Rect& rect, const PixelBuffer *pb) | |||
// Special exception inherited from the Tight encoder | |||
if (activeEncoders[encoderFullColour] == encoderTightJPEG) { | |||
if ((conn->cp.compressLevel != -1) && (conn->cp.compressLevel < 2)) | |||
if ((conn->client.compressLevel != -1) && (conn->client.compressLevel < 2)) | |||
maxColours = 24; | |||
else | |||
maxColours = 96; | |||
@@ -992,8 +996,8 @@ PixelBuffer* EncodeManager::preparePixelBuffer(const Rect& rect, | |||
int stride; | |||
// 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()); | |||
buffer = pb->getBuffer(rect, &stride); |
@@ -20,7 +20,7 @@ | |||
#include <rdr/MemInStream.h> | |||
#include <rdr/OutStream.h> | |||
#include <rfb/ConnParams.h> | |||
#include <rfb/ServerParams.h> | |||
#include <rfb/PixelBuffer.h> | |||
#include <rfb/HextileDecoder.h> | |||
@@ -45,12 +45,12 @@ HextileDecoder::~HextileDecoder() | |||
} | |||
void HextileDecoder::readRect(const Rect& r, rdr::InStream* is, | |||
const ConnParams& cp, rdr::OutStream* os) | |||
const ServerParams& server, rdr::OutStream* os) | |||
{ | |||
Rect t; | |||
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) { | |||
@@ -91,11 +91,11 @@ void HextileDecoder::readRect(const Rect& r, rdr::InStream* is, | |||
} | |||
void HextileDecoder::decodeRect(const Rect& r, const void* buffer, | |||
size_t buflen, const ConnParams& cp, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb) | |||
{ | |||
rdr::MemInStream is(buffer, buflen); | |||
const PixelFormat& pf = cp.pf(); | |||
const PixelFormat& pf = server.pf(); | |||
switch (pf.bpp) { | |||
case 8: hextileDecode8 (r, &is, pf, pb); break; | |||
case 16: hextileDecode16(r, &is, pf, pb); break; |
@@ -27,9 +27,9 @@ namespace rfb { | |||
HextileDecoder(); | |||
virtual ~HextileDecoder(); | |||
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, | |||
size_t buflen, const ConnParams& cp, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb); | |||
}; | |||
} |
@@ -55,7 +55,7 @@ HextileEncoder::~HextileEncoder() | |||
bool HextileEncoder::isSupported() | |||
{ | |||
return conn->cp.supportsEncoding(encodingHextile); | |||
return conn->client.supportsEncoding(encodingHextile); | |||
} | |||
void HextileEncoder::writeRect(const PixelBuffer* pb, const Palette& palette) |
@@ -22,7 +22,7 @@ | |||
#include <rdr/Exception.h> | |||
#include <rfb/Rect.h> | |||
#include <rfb/PixelFormat.h> | |||
#include <rfb/ConnParams.h> | |||
#include <rfb/ClientParams.h> | |||
#include <stdio.h> | |||
extern "C" { |
@@ -47,7 +47,16 @@ void Logger::write(int level, const char *logname, const char* format, | |||
char buf1[4096]; | |||
vsnprintf(buf1, sizeof(buf1)-1, format, ap); | |||
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 |
@@ -20,7 +20,7 @@ | |||
#include <rdr/MemInStream.h> | |||
#include <rdr/OutStream.h> | |||
#include <rfb/ConnParams.h> | |||
#include <rfb/ServerParams.h> | |||
#include <rfb/PixelBuffer.h> | |||
#include <rfb/RREDecoder.h> | |||
@@ -45,22 +45,22 @@ RREDecoder::~RREDecoder() | |||
} | |||
void RREDecoder::readRect(const Rect& r, rdr::InStream* is, | |||
const ConnParams& cp, rdr::OutStream* os) | |||
const ServerParams& server, rdr::OutStream* os) | |||
{ | |||
rdr::U32 numRects; | |||
numRects = is->readU32(); | |||
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, | |||
size_t buflen, const ConnParams& cp, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb) | |||
{ | |||
rdr::MemInStream is(buffer, buflen); | |||
const PixelFormat& pf = cp.pf(); | |||
const PixelFormat& pf = server.pf(); | |||
switch (pf.bpp) { | |||
case 8: rreDecode8 (r, &is, pf, pb); break; | |||
case 16: rreDecode16(r, &is, pf, pb); break; |
@@ -27,9 +27,9 @@ namespace rfb { | |||
RREDecoder(); | |||
virtual ~RREDecoder(); | |||
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, | |||
size_t buflen, const ConnParams& cp, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb); | |||
}; | |||
} |
@@ -47,7 +47,7 @@ RREEncoder::~RREEncoder() | |||
bool RREEncoder::isSupported() | |||
{ | |||
return conn->cp.supportsEncoding(encodingRRE); | |||
return conn->client.supportsEncoding(encodingRRE); | |||
} | |||
void RREEncoder::writeRect(const PixelBuffer* pb, const Palette& palette) |
@@ -19,7 +19,7 @@ | |||
#include <assert.h> | |||
#include <rdr/OutStream.h> | |||
#include <rfb/ConnParams.h> | |||
#include <rfb/ServerParams.h> | |||
#include <rfb/PixelBuffer.h> | |||
#include <rfb/RawDecoder.h> | |||
@@ -34,15 +34,15 @@ RawDecoder::~RawDecoder() | |||
} | |||
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, | |||
size_t buflen, const ConnParams& cp, | |||
size_t buflen, const ServerParams& server, | |||
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); | |||
} |
@@ -26,9 +26,9 @@ namespace rfb { | |||
RawDecoder(); | |||
virtual ~RawDecoder(); | |||
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, | |||
size_t buflen, const ConnParams& cp, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb); | |||
}; | |||
} |
@@ -59,7 +59,7 @@ SConnection::SConnection() | |||
if (rfb::Server::protocol3_3) | |||
defaultMinorVersion = 3; | |||
cp.setVersion(defaultMajorVersion, defaultMinorVersion); | |||
client.setVersion(defaultMajorVersion, defaultMinorVersion); | |||
} | |||
SConnection::~SConnection() | |||
@@ -80,7 +80,12 @@ void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_) | |||
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; | |||
} | |||
@@ -104,35 +109,47 @@ void SConnection::processMsg() | |||
void SConnection::processVersionMsg() | |||
{ | |||
char verStr[13]; | |||
int majorVersion; | |||
int minorVersion; | |||
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; | |||
throw Exception("reading version failed: not an RFB client?"); | |||
} | |||
if (!done) return; | |||
client.setVersion(majorVersion, minorVersion); | |||
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 | |||
throwConnFailedException("Client needs protocol version %d.%d, server has %d.%d", | |||
cp.majorVersion, cp.minorVersion, | |||
client.majorVersion, client.minorVersion, | |||
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", | |||
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 | |||
cp.minorVersion = 3; | |||
client.minorVersion = 3; | |||
vlog.error("Assuming compatibility with version %d.%d", | |||
cp.majorVersion,cp.minorVersion); | |||
client.majorVersion,client.minorVersion); | |||
} | |||
versionReceived(); | |||
@@ -141,7 +158,7 @@ void SConnection::processVersionMsg() | |||
std::list<rdr::U8>::iterator i; | |||
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 | |||
// authentication" is supported. | |||
@@ -150,7 +167,7 @@ void SConnection::processVersionMsg() | |||
} | |||
if (i == secTypes.end()) { | |||
throwConnFailedException("No supported security type for %d.%d client", | |||
cp.majorVersion, cp.minorVersion); | |||
client.majorVersion, client.minorVersion); | |||
} | |||
os->writeU32(*i); | |||
@@ -220,7 +237,7 @@ void SConnection::processSecurityMsg() | |||
} catch (AuthFailureException& e) { | |||
vlog.error("AuthFailureException: %s", e.str()); | |||
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->flush(); | |||
throw; | |||
@@ -245,7 +262,7 @@ void SConnection::throwConnFailedException(const char* format, ...) | |||
vlog.info("Connection failed: %s", str); | |||
if (state_ == RFBSTATE_PROTOCOL_VERSION) { | |||
if (cp.majorVersion == 3 && cp.minorVersion == 3) { | |||
if (client.majorVersion == 3 && client.minorVersion == 3) { | |||
os->writeU32(0); | |||
os->writeString(str); | |||
os->flush(); | |||
@@ -308,12 +325,12 @@ void SConnection::approveConnection(bool accept, const char* reason) | |||
if (state_ != RFBSTATE_QUERYING) | |||
throw Exception("SConnection::approveConnection: invalid state"); | |||
if (!cp.beforeVersion(3,8) || ssecurity->getType() != secTypeNone) { | |||
if (!client.beforeVersion(3,8) || ssecurity->getType() != secTypeNone) { | |||
if (accept) { | |||
os->writeU32(secResultOK); | |||
} else { | |||
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) | |||
os->writeString(reason); | |||
else | |||
@@ -326,7 +343,7 @@ void SConnection::approveConnection(bool accept, const char* reason) | |||
if (accept) { | |||
state_ = RFBSTATE_INITIALISATION; | |||
reader_ = new SMsgReader(this, is); | |||
writer_ = new SMsgWriter(&cp, os); | |||
writer_ = new SMsgWriter(&client, os); | |||
authSuccess(); | |||
} else { | |||
state_ = RFBSTATE_INVALID; | |||
@@ -339,7 +356,8 @@ void SConnection::approveConnection(bool accept, const char* reason) | |||
void SConnection::clientInit(bool shared) | |||
{ | |||
writer_->writeServerInit(); | |||
writer_->writeServerInit(client.width(), client.height(), | |||
client.pf(), client.name()); | |||
state_ = RFBSTATE_NORMAL; | |||
} | |||
@@ -360,7 +378,7 @@ void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental) | |||
{ | |||
if (!readyForSetColourMapEntries) { | |||
readyForSetColourMapEntries = true; | |||
if (!cp.pf().trueColour) { | |||
if (!client.pf().trueColour) { | |||
writeFakeColourMap(); | |||
} | |||
} | |||
@@ -388,7 +406,7 @@ void SConnection::writeFakeColourMap(void) | |||
rdr::U16 red[256], green[256], blue[256]; | |||
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); | |||
} |
@@ -19,6 +19,7 @@ | |||
#include <rfb/Exception.h> | |||
#include <rfb/SMsgHandler.h> | |||
#include <rfb/ScreenSet.h> | |||
#include <rfb/encodings.h> | |||
using namespace rfb; | |||
@@ -36,7 +37,7 @@ void SMsgHandler::clientInit(bool shared) | |||
void SMsgHandler::setPixelFormat(const PixelFormat& pf) | |||
{ | |||
cp.setPF(pf); | |||
client.setPF(pf); | |||
} | |||
void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings) | |||
@@ -44,22 +45,22 @@ void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings) | |||
bool firstFence, firstContinuousUpdates, firstLEDState, | |||
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(); | |||
if (cp.supportsFence && firstFence) | |||
if (client.supportsFence() && firstFence) | |||
supportsFence(); | |||
if (cp.supportsContinuousUpdates && firstContinuousUpdates) | |||
if (client.supportsContinuousUpdates() && firstContinuousUpdates) | |||
supportsContinuousUpdates(); | |||
if (cp.supportsLEDState && firstLEDState) | |||
if (client.supportsLEDState() && firstLEDState) | |||
supportsLEDState(); | |||
if (cp.supportsQEMUKeyEvent && firstQEMUKeyEvent) | |||
if (client.supportsEncoding(pseudoEncodingQEMUKeyEvent) && firstQEMUKeyEvent) | |||
supportsQEMUKeyEvent(); | |||
} | |||
@@ -82,12 +83,3 @@ void SMsgHandler::supportsLEDState() | |||
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; | |||
} | |||
@@ -25,7 +25,7 @@ | |||
#include <rdr/types.h> | |||
#include <rfb/PixelFormat.h> | |||
#include <rfb/ConnParams.h> | |||
#include <rfb/ClientParams.h> | |||
#include <rfb/InputHandler.h> | |||
#include <rfb/ScreenSet.h> | |||
@@ -40,8 +40,8 @@ namespace rfb { | |||
// The following methods are called as corresponding messages are read. A | |||
// 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); | |||
@@ -85,7 +85,7 @@ namespace rfb { | |||
// handler will send a pseudo-rect back, signalling server support. | |||
virtual void supportsQEMUKeyEvent(); | |||
ConnParams cp; | |||
ClientParams client; | |||
}; | |||
} | |||
#endif |
@@ -22,7 +22,7 @@ | |||
#include <rfb/msgTypes.h> | |||
#include <rfb/fenceTypes.h> | |||
#include <rfb/Exception.h> | |||
#include <rfb/ConnParams.h> | |||
#include <rfb/ClientParams.h> | |||
#include <rfb/UpdateTracker.h> | |||
#include <rfb/Encoder.h> | |||
#include <rfb/SMsgWriter.h> | |||
@@ -33,12 +33,10 @@ using namespace rfb; | |||
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), | |||
needSetDesktopSize(false), needExtendedDesktopSize(false), | |||
needSetDesktopName(false), needSetCursor(false), | |||
needSetXCursor(false), needSetCursorWithAlpha(false), | |||
needSetDesktopName(false), needCursor(false), | |||
needLEDState(false), needQEMUKeyEvent(false) | |||
{ | |||
} | |||
@@ -47,12 +45,13 @@ SMsgWriter::~SMsgWriter() | |||
{ | |||
} | |||
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(); | |||
} | |||
@@ -90,7 +89,7 @@ void SMsgWriter::writeServerCutText(const char* str, int len) | |||
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"); | |||
if (len > 64) | |||
throw Exception("Too large fence payload"); | |||
@@ -112,116 +111,68 @@ void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[]) | |||
void SMsgWriter::writeEndOfContinuousUpdates() | |||
{ | |||
if (!cp->supportsContinuousUpdates) | |||
if (!client->supportsEncoding(pseudoEncodingContinuousUpdates)) | |||
throw Exception("Client does not support continuous updates"); | |||
startMsg(msgTypeEndOfContinuousUpdates); | |||
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; | |||
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.result = result; | |||
msg.fb_width = fb_width; | |||
msg.fb_height = fb_height; | |||
msg.layout = layout; | |||
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; | |||
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; | |||
return true; | |||
} | |||
bool SMsgWriter::needFakeUpdate() | |||
{ | |||
if (needSetDesktopName) | |||
return true; | |||
if (needSetCursor || needSetXCursor || needSetCursorWithAlpha) | |||
if (needCursor) | |||
return true; | |||
if (needLEDState) | |||
return true; | |||
@@ -235,9 +186,7 @@ bool SMsgWriter::needFakeUpdate() | |||
bool SMsgWriter::needNoDataUpdate() | |||
{ | |||
if (needSetDesktopSize) | |||
return true; | |||
if (needExtendedDesktopSize || !extendedDesktopSizeMsgs.empty()) | |||
if (!extendedDesktopSizeMsgs.empty()) | |||
return true; | |||
return false; | |||
@@ -249,12 +198,12 @@ void SMsgWriter::writeNoDataUpdate() | |||
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); | |||
writeNoDataRects(); | |||
@@ -269,11 +218,7 @@ void SMsgWriter::writeFramebufferUpdateStart(int nRects) | |||
if (nRects != 0xFFFF) { | |||
if (needSetDesktopName) | |||
nRects++; | |||
if (needSetCursor) | |||
nRects++; | |||
if (needSetXCursor) | |||
nRects++; | |||
if (needSetCursorWithAlpha) | |||
if (needCursor) | |||
nRects++; | |||
if (needLEDState) | |||
nRects++; | |||
@@ -347,56 +292,52 @@ void SMsgWriter::endMsg() | |||
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) { | |||
writeSetDesktopNameRect(cp->name()); | |||
writeSetDesktopNameRect(client->name()); | |||
needSetDesktopName = false; | |||
} | |||
if (needLEDState) { | |||
writeLEDStateRect(cp->ledState()); | |||
writeLEDStateRect(client->ledState()); | |||
needLEDState = false; | |||
} | |||
@@ -408,36 +349,30 @@ void SMsgWriter::writePseudoRects() | |||
void SMsgWriter::writeNoDataRects() | |||
{ | |||
// Start with specific ExtendedDesktopSize messages | |||
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(); | |||
} | |||
// 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) | |||
{ | |||
if (!cp->supportsDesktopResize) | |||
if (!client->supportsEncoding(pseudoEncodingDesktopSize)) | |||
throw Exception("Client does not support desktop resize"); | |||
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) | |||
throw Exception("SMsgWriter::writeSetDesktopSizeRect: nRects out of sync"); | |||
@@ -457,7 +392,7 @@ void SMsgWriter::writeExtendedDesktopSizeRect(rdr::U16 reason, | |||
{ | |||
ScreenSet::const_iterator si; | |||
if (!cp->supportsExtendedDesktopSize) | |||
if (!client->supportsEncoding(pseudoEncodingExtendedDesktopSize)) | |||
throw Exception("Client does not support extended desktop resize"); | |||
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) | |||
throw Exception("SMsgWriter::writeExtendedDesktopSizeRect: nRects out of sync"); | |||
@@ -483,7 +418,7 @@ void SMsgWriter::writeExtendedDesktopSizeRect(rdr::U16 reason, | |||
void SMsgWriter::writeSetDesktopNameRect(const char *name) | |||
{ | |||
if (!cp->supportsDesktopRename) | |||
if (!client->supportsEncoding(pseudoEncodingDesktopName)) | |||
throw Exception("Client does not support desktop rename"); | |||
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) | |||
throw Exception("SMsgWriter::writeSetDesktopNameRect: nRects out of sync"); | |||
@@ -500,7 +435,7 @@ void SMsgWriter::writeSetCursorRect(int width, int height, | |||
int hotspotX, int hotspotY, | |||
const void* data, const void* mask) | |||
{ | |||
if (!cp->supportsLocalCursor) | |||
if (!client->supportsEncoding(pseudoEncodingCursor)) | |||
throw Exception("Client does not support local cursors"); | |||
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) | |||
throw Exception("SMsgWriter::writeSetCursorRect: nRects out of sync"); | |||
@@ -510,7 +445,7 @@ void SMsgWriter::writeSetCursorRect(int width, int height, | |||
os->writeU16(width); | |||
os->writeU16(height); | |||
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); | |||
} | |||
@@ -518,7 +453,7 @@ void SMsgWriter::writeSetXCursorRect(int width, int height, | |||
int hotspotX, int hotspotY, | |||
const void* data, const void* mask) | |||
{ | |||
if (!cp->supportsLocalXCursor) | |||
if (!client->supportsEncoding(pseudoEncodingXCursor)) | |||
throw Exception("Client does not support local cursors"); | |||
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) | |||
throw Exception("SMsgWriter::writeSetXCursorRect: nRects out of sync"); | |||
@@ -544,7 +479,7 @@ void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height, | |||
int hotspotX, int hotspotY, | |||
const rdr::U8* data) | |||
{ | |||
if (!cp->supportsLocalCursorWithAlpha) | |||
if (!client->supportsEncoding(pseudoEncodingCursorWithAlpha)) | |||
throw Exception("Client does not support local cursors"); | |||
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) | |||
throw Exception("SMsgWriter::writeSetCursorWithAlphaRect: nRects out of sync"); | |||
@@ -570,9 +505,9 @@ void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height, | |||
void SMsgWriter::writeLEDStateRect(rdr::U8 state) | |||
{ | |||
if (!cp->supportsLEDState) | |||
if (!client->supportsEncoding(pseudoEncodingLEDState)) | |||
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"); | |||
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) | |||
throw Exception("SMsgWriter::writeLEDStateRect: nRects out of sync"); | |||
@@ -587,7 +522,7 @@ void SMsgWriter::writeLEDStateRect(rdr::U8 state) | |||
void SMsgWriter::writeQEMUKeyEventRect() | |||
{ | |||
if (!cp->supportsQEMUKeyEvent) | |||
if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent)) | |||
throw Exception("Client does not support QEMU extended key events"); | |||
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) | |||
throw Exception("SMsgWriter::writeQEMUKeyEventRect: nRects out of sync"); |
@@ -31,17 +31,19 @@ namespace rdr { class OutStream; } | |||
namespace rfb { | |||
class ConnParams; | |||
class ClientParams; | |||
class PixelFormat; | |||
struct ScreenSet; | |||
class SMsgWriter { | |||
public: | |||
SMsgWriter(ConnParams* cp, rdr::OutStream* os); | |||
SMsgWriter(ClientParams* client, rdr::OutStream* os); | |||
virtual ~SMsgWriter(); | |||
// writeServerInit() must only be called at the appropriate time in the | |||
// protocol initialisation. | |||
void writeServerInit(); | |||
void writeServerInit(rdr::U16 width, rdr::U16 height, | |||
const PixelFormat& pf, const char* name); | |||
// Methods to write normal protocol messages | |||
@@ -63,30 +65,21 @@ namespace rfb { | |||
// updates mode. | |||
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. | |||
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 | |||
// immediately. | |||
bool writeSetCursor(); | |||
bool writeSetXCursor(); | |||
bool writeSetCursorWithAlpha(); | |||
void writeCursor(); | |||
// Same for LED state message | |||
bool writeLEDState(); | |||
void writeLEDState(); | |||
// And QEMU keyboard event handshake | |||
bool writeQEMUKeyEvent(); | |||
void writeQEMUKeyEvent(); | |||
// needFakeUpdate() returns true when an immediate update is needed in | |||
// order to flush out pseudo-rectangles to the client. | |||
@@ -140,25 +133,19 @@ namespace rfb { | |||
void writeLEDStateRect(rdr::U8 state); | |||
void writeQEMUKeyEventRect(); | |||
ConnParams* cp; | |||
ClientParams* client; | |||
rdr::OutStream* os; | |||
int nRectsInUpdate; | |||
int nRectsInHeader; | |||
bool needSetDesktopSize; | |||
bool needExtendedDesktopSize; | |||
bool needSetDesktopName; | |||
bool needSetCursor; | |||
bool needSetXCursor; | |||
bool needSetCursorWithAlpha; | |||
bool needCursor; | |||
bool needLEDState; | |||
bool needQEMUKeyEvent; | |||
typedef struct { | |||
rdr::U16 reason, result; | |||
int fb_width, fb_height; | |||
ScreenSet layout; | |||
} ExtendedDesktopSizeMsg; | |||
std::list<ExtendedDesktopSizeMsg> extendedDesktopSizeMsgs; |
@@ -0,0 +1,84 @@ | |||
/* 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; | |||
} |
@@ -0,0 +1,89 @@ | |||
/* 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 |
@@ -25,7 +25,7 @@ | |||
#include <rdr/MemInStream.h> | |||
#include <rdr/OutStream.h> | |||
#include <rfb/ConnParams.h> | |||
#include <rfb/ServerParams.h> | |||
#include <rfb/Exception.h> | |||
#include <rfb/PixelBuffer.h> | |||
#include <rfb/TightConstants.h> | |||
@@ -55,7 +55,7 @@ TightDecoder::~TightDecoder() | |||
} | |||
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; | |||
@@ -66,10 +66,10 @@ void TightDecoder::readRect(const Rect& r, rdr::InStream* is, | |||
// "Fill" compression type. | |||
if (comp_ctl == tightFill) { | |||
if (cp.pf().is888()) | |||
if (server.pf().is888()) | |||
os->copyBytes(is, 3); | |||
else | |||
os->copyBytes(is, cp.pf().bpp/8); | |||
os->copyBytes(is, server.pf().bpp/8); | |||
return; | |||
} | |||
@@ -106,13 +106,13 @@ void TightDecoder::readRect(const Rect& r, rdr::InStream* is, | |||
palSize = is->readU8() + 1; | |||
os->writeU8(palSize - 1); | |||
if (cp.pf().is888()) | |||
if (server.pf().is888()) | |||
os->copyBytes(is, palSize * 3); | |||
else | |||
os->copyBytes(is, palSize * cp.pf().bpp/8); | |||
os->copyBytes(is, palSize * server.pf().bpp/8); | |||
break; | |||
case tightFilterGradient: | |||
if (cp.pf().bpp == 8) | |||
if (server.pf().bpp == 8) | |||
throw Exception("TightDecoder: invalid BPP for gradient filter"); | |||
break; | |||
case tightFilterCopy: | |||
@@ -129,10 +129,10 @@ void TightDecoder::readRect(const Rect& r, rdr::InStream* is, | |||
rowSize = (r.width() + 7) / 8; | |||
else | |||
rowSize = r.width(); | |||
} else if (cp.pf().is888()) { | |||
} else if (server.pf().is888()) { | |||
rowSize = r.width() * 3; | |||
} else { | |||
rowSize = r.width() * cp.pf().bpp/8; | |||
rowSize = r.width() * server.pf().bpp/8; | |||
} | |||
dataSize = r.height() * rowSize; | |||
@@ -154,7 +154,7 @@ bool TightDecoder::doRectsConflict(const Rect& rectA, | |||
const Rect& rectB, | |||
const void* bufferB, | |||
size_t buflenB, | |||
const ConnParams& cp) | |||
const ServerParams& server) | |||
{ | |||
rdr::U8 comp_ctl_a, comp_ctl_b; | |||
@@ -177,11 +177,11 @@ bool TightDecoder::doRectsConflict(const Rect& rectA, | |||
} | |||
void TightDecoder::decodeRect(const Rect& r, const void* buffer, | |||
size_t buflen, const ConnParams& cp, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb) | |||
{ | |||
const rdr::U8* bufptr; | |||
const PixelFormat& pf = cp.pf(); | |||
const PixelFormat& pf = server.pf(); | |||
rdr::U8 comp_ctl; | |||
@@ -32,16 +32,16 @@ namespace rfb { | |||
TightDecoder(); | |||
virtual ~TightDecoder(); | |||
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, | |||
const void* bufferA, | |||
size_t buflenA, | |||
const Rect& rectB, | |||
const void* bufferB, | |||
size_t buflenB, | |||
const ConnParams& cp); | |||
const ServerParams& server); | |||
virtual void decodeRect(const Rect& r, const void* buffer, | |||
size_t buflen, const ConnParams& cp, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb); | |||
private: |
@@ -23,7 +23,6 @@ | |||
#include <rfb/PixelBuffer.h> | |||
#include <rfb/Palette.h> | |||
#include <rfb/encodings.h> | |||
#include <rfb/ConnParams.h> | |||
#include <rfb/SConnection.h> | |||
#include <rfb/TightEncoder.h> | |||
#include <rfb/TightConstants.h> | |||
@@ -68,7 +67,7 @@ TightEncoder::~TightEncoder() | |||
bool TightEncoder::isSupported() | |||
{ | |||
return conn->cp.supportsEncoding(encodingTight); | |||
return conn->client.supportsEncoding(encodingTight); | |||
} | |||
void TightEncoder::setCompressLevel(int level) |
@@ -76,15 +76,15 @@ TightJPEGEncoder::~TightJPEGEncoder() | |||
bool TightJPEGEncoder::isSupported() | |||
{ | |||
if (!conn->cp.supportsEncoding(encodingTight)) | |||
if (!conn->client.supportsEncoding(encodingTight)) | |||
return false; | |||
// Any one of these indicates support for JPEG | |||
if (conn->cp.qualityLevel != -1) | |||
if (conn->client.qualityLevel != -1) | |||
return true; | |||
if (conn->cp.fineQualityLevel != -1) | |||
if (conn->client.fineQualityLevel != -1) | |||
return true; | |||
if (conn->cp.subsampling != -1) | |||
if (conn->client.subsampling != -1) | |||
return true; | |||
// Tight support, but not JPEG |
@@ -60,32 +60,17 @@ void ClippingUpdateTracker::add_copied(const Region &dest, const Point &delta) { | |||
// SimpleUpdateTracker | |||
SimpleUpdateTracker::SimpleUpdateTracker(bool use_copyrect) { | |||
copy_enabled = use_copyrect; | |||
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 ®ion) { | |||
changed.assign_union(region); | |||
} | |||
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? | |||
if (dest.is_empty()) return; | |||
@@ -68,11 +68,9 @@ namespace rfb { | |||
class SimpleUpdateTracker : public UpdateTracker { | |||
public: | |||
SimpleUpdateTracker(bool use_copyrect=true); | |||
SimpleUpdateTracker(); | |||
virtual ~SimpleUpdateTracker(); | |||
virtual void enable_copyrect(bool enable); | |||
virtual void add_changed(const Region ®ion); | |||
virtual void add_copied(const Region &dest, const Point &delta); | |||
virtual void subtract(const Region& region); | |||
@@ -94,7 +92,6 @@ namespace rfb { | |||
Region changed; | |||
Region copied; | |||
Point copy_delta; | |||
bool copy_enabled; | |||
}; | |||
} |
@@ -49,7 +49,7 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, | |||
inProcessMessages(false), | |||
pendingSyncFence(false), syncFence(false), fenceFlags(0), | |||
fenceDataLen(0), fenceData(NULL), congestionTimer(this), | |||
losslessTimer(this), server(server_), updates(false), | |||
losslessTimer(this), server(server_), | |||
updateRenderedCursor(false), removeRenderedCursor(false), | |||
continuousUpdates(false), encodeManager(this), idleTimer(this), | |||
pointerEventTime(0), clientHasCursor(false) | |||
@@ -199,9 +199,9 @@ void VNCSConnectionST::pixelBufferChange() | |||
{ | |||
try { | |||
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 | |||
// extra bits if it's bigger. If we wanted to do this exactly, something | |||
@@ -211,26 +211,24 @@ void VNCSConnectionST::pixelBufferChange() | |||
//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())); | |||
//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())); | |||
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) { | |||
// 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 | |||
@@ -325,7 +323,7 @@ bool VNCSConnectionST::getComparerState() | |||
// We interpret a low compression level as an indication that the client | |||
// wants to prioritise CPU usage over bandwidth, and hence disable the | |||
// comparing update tracker. | |||
return (cp.compressLevel == -1) || (cp.compressLevel > 1); | |||
return (client.compressLevel == -1) || (client.compressLevel > 1); | |||
} | |||
@@ -363,8 +361,7 @@ bool VNCSConnectionST::needRenderedCursor() | |||
if (state() != RFBSTATE_NORMAL) | |||
return false; | |||
if (!cp.supportsLocalCursorWithAlpha && | |||
!cp.supportsLocalCursor && !cp.supportsLocalXCursor) | |||
if (!client.supportsLocalCursor()) | |||
return true; | |||
if (!server->getCursorPos().equals(pointerEventPos) && | |||
(time(0) - pointerEventTime) > 0) | |||
@@ -394,16 +391,16 @@ void VNCSConnectionST::authSuccess() | |||
idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); | |||
// - 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 | |||
cp.setPF(server->getPixelBuffer()->getPF()); | |||
client.setPF(server->getPixelBuffer()->getPF()); | |||
char buffer[256]; | |||
cp.pf().print(buffer, 256); | |||
client.pf().print(buffer, 256); | |||
vlog.info("Server default pixel format %s", buffer); | |||
// - Mark the entire display as "dirty" | |||
@@ -492,7 +489,7 @@ void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) { | |||
// Lock key heuristics | |||
// (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 | |||
// for that | |||
if (keysym == XK_Scroll_Lock) { | |||
@@ -597,10 +594,11 @@ void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental) | |||
SConnection::framebufferUpdateRequest(r, incremental); | |||
// 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", | |||
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 { | |||
safeRect = r; | |||
} | |||
@@ -617,7 +615,8 @@ void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental) | |||
// And send the screen layout to the client (which, unlike the | |||
// 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 | |||
// framebuffer size (which the client already should know) and | |||
@@ -635,8 +634,7 @@ void VNCSConnectionST::setDesktopSize(int fb_width, int fb_height, | |||
if (!rfb::Server::acceptSetDesktopSize) return; | |||
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[]) | |||
@@ -688,7 +686,7 @@ void VNCSConnectionST::enableContinuousUpdates(bool enable, | |||
{ | |||
Rect rect; | |||
if (!cp.supportsFence || !cp.supportsContinuousUpdates) | |||
if (!client.supportsFence() || !client.supportsContinuousUpdates()) | |||
throw Exception("Client tried to enable continuous updates when not allowed"); | |||
continuousUpdates = enable; | |||
@@ -704,7 +702,7 @@ void VNCSConnectionST::enableContinuousUpdates(bool enable, | |||
} | |||
// 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 | |||
// and the cursor is sent to the client. | |||
@@ -726,7 +724,7 @@ void VNCSConnectionST::supportsContinuousUpdates() | |||
{ | |||
// We refuse to use continuous updates if we cannot monitor the buffer | |||
// usage using fences. | |||
if (!cp.supportsFence) | |||
if (!client.supportsFence()) | |||
return; | |||
writer()->writeEndOfContinuousUpdates(); | |||
@@ -734,6 +732,9 @@ void VNCSConnectionST::supportsContinuousUpdates() | |||
void VNCSConnectionST::supportsLEDState() | |||
{ | |||
if (client.ledState() == ledUnknown) | |||
return; | |||
writer()->writeLEDState(); | |||
} | |||
@@ -772,7 +773,7 @@ void VNCSConnectionST::writeRTTPing() | |||
{ | |||
char type; | |||
if (!cp.supportsFence) | |||
if (!client.supportsFence()) | |||
return; | |||
congestion.updatePosition(sock->outStream().length()); | |||
@@ -799,7 +800,7 @@ bool VNCSConnectionST::isCongested() | |||
if (sock->outStream().bufferUsage() > 0) | |||
return true; | |||
if (!cp.supportsFence) | |||
if (!client.supportsFence()) | |||
return false; | |||
congestion.updatePosition(sock->outStream().length()); | |||
@@ -876,8 +877,6 @@ void VNCSConnectionST::writeDataUpdate() | |||
bool needNewUpdateInfo; | |||
const RenderedCursor *cursor; | |||
updates.enable_copyrect(cp.useCopyRect); | |||
// See what the client has requested (if anything) | |||
if (continuousUpdates) | |||
req = cuRegion.union_(requested); | |||
@@ -1063,13 +1062,13 @@ void VNCSConnectionST::screenLayoutChange(rdr::U16 reason) | |||
if (!authenticated()) | |||
return; | |||
cp.screenLayout = server->getScreenLayout(); | |||
client.setDimensions(client.width(), client.height(), | |||
server->getScreenLayout()); | |||
if (state() != RFBSTATE_NORMAL) | |||
return; | |||
writer()->writeExtendedDesktopSize(reason, 0, cp.width, cp.height, | |||
cp.screenLayout); | |||
writer()->writeDesktopSize(reason); | |||
} | |||
@@ -1084,34 +1083,26 @@ void VNCSConnectionST::setCursor() | |||
// We need to blank out the client's cursor or there will be two | |||
if (needRenderedCursor()) { | |||
cp.setCursor(emptyCursor); | |||
client.setCursor(emptyCursor); | |||
clientHasCursor = false; | |||
} else { | |||
cp.setCursor(*server->getCursor()); | |||
client.setCursor(*server->getCursor()); | |||
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) | |||
{ | |||
cp.setName(name); | |||
client.setName(name); | |||
if (state() != RFBSTATE_NORMAL) | |||
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) | |||
@@ -1119,9 +1110,10 @@ void VNCSConnectionST::setLEDState(unsigned int ledstate) | |||
if (state() != RFBSTATE_NORMAL) | |||
return; | |||
cp.setLEDState(ledstate); | |||
client.setLEDState(ledstate); | |||
writer()->writeLEDState(); | |||
if (client.supportsLEDState()) | |||
writer()->writeLEDState(); | |||
} | |||
void VNCSConnectionST::setSocketTimeouts() |
@@ -255,8 +255,6 @@ void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout) | |||
delete comparer; | |||
comparer = 0; | |||
screenLayout = layout; | |||
if (!pb) { | |||
screenLayout = ScreenSet(); | |||
@@ -266,16 +264,17 @@ void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout) | |||
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 | |||
// that tracks its contents | |||
comparer = new ComparingUpdateTracker(pb); | |||
renderedCursorInvalid = true; | |||
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; | |||
for (ci=clients.begin();ci!=clients.end();ci=ci_next) { | |||
ci_next = ci; ci_next++; | |||
@@ -309,6 +308,10 @@ void VNCServerST::setPixelBuffer(PixelBuffer* pb_) | |||
} | |||
} | |||
// 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); | |||
} | |||
@@ -21,7 +21,7 @@ | |||
#include <rdr/MemInStream.h> | |||
#include <rdr/OutStream.h> | |||
#include <rfb/ConnParams.h> | |||
#include <rfb/ServerParams.h> | |||
#include <rfb/PixelBuffer.h> | |||
#include <rfb/ZRLEDecoder.h> | |||
@@ -72,7 +72,7 @@ ZRLEDecoder::~ZRLEDecoder() | |||
} | |||
void ZRLEDecoder::readRect(const Rect& r, rdr::InStream* is, | |||
const ConnParams& cp, rdr::OutStream* os) | |||
const ServerParams& server, rdr::OutStream* os) | |||
{ | |||
rdr::U32 len; | |||
@@ -82,11 +82,11 @@ void ZRLEDecoder::readRect(const Rect& r, rdr::InStream* is, | |||
} | |||
void ZRLEDecoder::decodeRect(const Rect& r, const void* buffer, | |||
size_t buflen, const ConnParams& cp, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb) | |||
{ | |||
rdr::MemInStream is(buffer, buflen); | |||
const rfb::PixelFormat& pf = cp.pf(); | |||
const rfb::PixelFormat& pf = server.pf(); | |||
switch (pf.bpp) { | |||
case 8: zrleDecode8 (r, &is, &zis, pf, pb); break; | |||
case 16: zrleDecode16(r, &is, &zis, pf, pb); break; |
@@ -28,9 +28,9 @@ namespace rfb { | |||
ZRLEDecoder(); | |||
virtual ~ZRLEDecoder(); | |||
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, | |||
size_t buflen, const ConnParams& cp, | |||
size_t buflen, const ServerParams& server, | |||
ModifiablePixelBuffer* pb); | |||
private: | |||
rdr::ZlibInStream zis; |
@@ -19,7 +19,6 @@ | |||
#include <rdr/OutStream.h> | |||
#include <rfb/Exception.h> | |||
#include <rfb/encodings.h> | |||
#include <rfb/ConnParams.h> | |||
#include <rfb/Palette.h> | |||
#include <rfb/SConnection.h> | |||
#include <rfb/ZRLEEncoder.h> | |||
@@ -43,7 +42,7 @@ ZRLEEncoder::~ZRLEEncoder() | |||
bool ZRLEEncoder::isSupported() | |||
{ | |||
return conn->cp.supportsEncoding(encodingZRLE); | |||
return conn->client.supportsEncoding(encodingZRLE); | |||
} | |||
void ZRLEEncoder::writeRect(const PixelBuffer* pb, const Palette& palette) |
@@ -47,7 +47,7 @@ public: | |||
CConn(const char *filename); | |||
~CConn(); | |||
virtual void setDesktopSize(int w, int h); | |||
virtual void initDone(); | |||
virtual void setPixelFormat(const rfb::PixelFormat& pf); | |||
virtual void setCursor(int, int, const rfb::Point&, const rdr::U8*); | |||
virtual void framebufferUpdateStart(); | |||
@@ -81,11 +81,11 @@ CConn::~CConn() | |||
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) |
@@ -89,7 +89,7 @@ public: | |||
void getStats(double& ratio, unsigned long long& bytes, | |||
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 framebufferUpdateStart(); | |||
virtual void framebufferUpdateEnd(); | |||
@@ -180,7 +180,7 @@ CConn::CConn(const char *filename) | |||
setDesktopSize(width, height); | |||
sc = new SConn(); | |||
sc->cp.setPF((bool)translate ? fbPF : pf); | |||
sc->client.setPF((bool)translate ? fbPF : pf); | |||
sc->setEncodings(sizeof(encodings) / sizeof(*encodings), encodings); | |||
} | |||
@@ -196,14 +196,12 @@ void CConn::getStats(double& ratio, unsigned long long& bytes, | |||
sc->getStats(ratio, bytes, rawEquivalent); | |||
} | |||
void CConn::setDesktopSize(int w, int h) | |||
void CConn::initDone() | |||
{ | |||
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); | |||
} | |||
@@ -290,7 +288,7 @@ SConn::SConn() | |||
out = new DummyOutStream; | |||
setStreams(NULL, out); | |||
setWriter(new rfb::SMsgWriter(&cp, out)); | |||
setWriter(new rfb::SMsgWriter(&client, out)); | |||
manager = new Manager(this); | |||
} |
@@ -423,8 +423,27 @@ ScreenSet XDesktop::computeScreenLayout() | |||
layout = ::computeScreenLayout(&outputIdMap); | |||
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 | |||
// 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; | |||
} | |||
@@ -36,8 +36,6 @@ | |||
#include <rfb/screenTypes.h> | |||
#include <rfb/fenceTypes.h> | |||
#include <rfb/Timer.h> | |||
#include <rdr/MemInStream.h> | |||
#include <rdr/MemOutStream.h> | |||
#include <network/TcpSocket.h> | |||
#ifndef WIN32 | |||
#include <network/UnixSocket.h> | |||
@@ -76,36 +74,21 @@ static const PixelFormat mediumColourPF(8, 8, false, true, | |||
CConn::CConn(const char* vncServerName, network::Socket* socket=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); | |||
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) | |||
cp.compressLevel = compressLevel; | |||
else | |||
cp.compressLevel = -1; | |||
setCompressLevel(::compressLevel); | |||
if (!noJpeg) | |||
cp.qualityLevel = qualityLevel; | |||
else | |||
cp.qualityLevel = -1; | |||
setQualityLevel(::qualityLevel); | |||
if(sock == NULL) { | |||
try { | |||
@@ -158,16 +141,6 @@ CConn::~CConn() | |||
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() | |||
{ | |||
static char infoText[1024] = ""; | |||
@@ -181,7 +154,7 @@ const char *CConn::connectionInfo() | |||
infoText[0] = '\0'; | |||
snprintf(scratch, sizeof(scratch), | |||
_("Desktop name: %.80s"), cp.name()); | |||
_("Desktop name: %.80s"), server.name()); | |||
strcat(infoText, scratch); | |||
strcat(infoText, "\n"); | |||
@@ -191,13 +164,13 @@ const char *CConn::connectionInfo() | |||
strcat(infoText, "\n"); | |||
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, "\n"); | |||
// TRANSLATORS: Will be filled in with a string describing the | |||
// protocol pixel format in a fairly language neutral way | |||
cp.pf().print(pfStr, 100); | |||
server.pf().print(pfStr, 100); | |||
snprintf(scratch, sizeof(scratch), | |||
_("Pixel format: %s"), pfStr); | |||
strcat(infoText, scratch); | |||
@@ -211,7 +184,7 @@ const char *CConn::connectionInfo() | |||
strcat(infoText, "\n"); | |||
snprintf(scratch, sizeof(scratch), | |||
_("Requested encoding: %s"), encodingName(currentEncoding)); | |||
_("Requested encoding: %s"), encodingName(getPreferredEncoding())); | |||
strcat(infoText, scratch); | |||
strcat(infoText, "\n"); | |||
@@ -226,7 +199,7 @@ const char *CConn::connectionInfo() | |||
strcat(infoText, "\n"); | |||
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, "\n"); | |||
@@ -310,34 +283,27 @@ void CConn::socketEvent(FL_SOCKET fd, void *data) | |||
////////////////////// 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 | |||
// 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 | |||
// mode. See comment in autoSelectFormatAndEncoding. | |||
if (cp.beforeVersion(3, 8) && autoSelect) | |||
if (server.beforeVersion(3, 8) && autoSelect) | |||
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(); | |||
// 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 | |||
@@ -366,8 +332,7 @@ void CConn::setExtendedDesktopSize(unsigned reason, unsigned result, | |||
void CConn::setName(const char* name) | |||
{ | |||
CConnection::setName(name); | |||
if (desktop) | |||
desktop->setName(name); | |||
desktop->setName(name); | |||
} | |||
// framebufferUpdateStart() is called at the beginning of an update. | |||
@@ -378,11 +343,6 @@ void CConn::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 | |||
Fl::add_timeout(1.0, handleUpdateTimeout, this); | |||
} | |||
@@ -400,22 +360,6 @@ void CConn::framebufferUpdateEnd() | |||
Fl::remove_timeout(handleUpdateTimeout, this); | |||
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 | |||
if (autoSelect) | |||
autoSelectFormatAndEncoding(); | |||
@@ -469,27 +413,6 @@ void CConn::fence(rdr::U32 flags, unsigned len, const char data[]) | |||
writer()->writeFence(flags, len, data); | |||
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) | |||
@@ -504,13 +427,7 @@ void CConn::setLEDState(unsigned int state) | |||
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 | |||
@@ -533,13 +450,10 @@ void CConn::autoSelectFormatAndEncoding() | |||
int kbitsPerSecond = sock->inStream().kbitsPerSecond(); | |||
unsigned int timeWaited = sock->inStream().timeWaited(); | |||
bool newFullColour = fullColour; | |||
int newQualityLevel = qualityLevel; | |||
int newQualityLevel = ::qualityLevel; | |||
// Always use Tight | |||
if (currentEncoding != encodingTight) { | |||
currentEncoding = encodingTight; | |||
encodingChange = true; | |||
} | |||
setPreferredEncoding(encodingTight); | |||
// Check that we have a decent bandwidth measurement | |||
if ((kbitsPerSecond == 0) || (timeWaited < 10000)) | |||
@@ -552,16 +466,15 @@ void CConn::autoSelectFormatAndEncoding() | |||
else | |||
newQualityLevel = 6; | |||
if (newQualityLevel != qualityLevel) { | |||
if (newQualityLevel != ::qualityLevel) { | |||
vlog.info(_("Throughput %d kbit/s - changing to quality %d"), | |||
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 | |||
// cursors "asynchronously". If this happens in the middle of a | |||
// pixel format change, the server will encode the cursor with | |||
@@ -582,76 +495,31 @@ void CConn::autoSelectFormatAndEncoding() | |||
vlog.info(_("Throughput %d kbit/s - full color is now disabled"), | |||
kbitsPerSecond); | |||
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 | |||
// 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) | |||
@@ -663,50 +531,23 @@ void CConn::handleOptions(void *data) | |||
// list is cheap. Avoid overriding what the auto logic has selected | |||
// though. | |||
if (!autoSelect) { | |||
int encNum = encodingNum(preferredEncoding); | |||
int encNum = encodingNum(::preferredEncoding); | |||
if (encNum != -1) | |||
self->currentEncoding = encNum; | |||
self->setPreferredEncoding(encNum); | |||
} | |||
self->cp.supportsLocalCursor = true; | |||
if (customCompressLevel) | |||
self->cp.compressLevel = compressLevel; | |||
self->setCompressLevel(::compressLevel); | |||
else | |||
self->cp.compressLevel = -1; | |||
self->setCompressLevel(-1); | |||
if (!noJpeg && !autoSelect) | |||
self->cp.qualityLevel = qualityLevel; | |||
self->setQualityLevel(::qualityLevel); | |||
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) |
@@ -36,8 +36,6 @@ public: | |||
CConn(const char* vncServerName, network::Socket* sock); | |||
~CConn(); | |||
void refreshFramebuffer(); | |||
const char *connectionInfo(); | |||
unsigned getUpdateCount(); | |||
@@ -51,7 +49,7 @@ public: | |||
static void socketEvent(FL_SOCKET fd, void *data); | |||
// CConnection callback methods | |||
void serverInit(); | |||
void initDone(); | |||
void setDesktopSize(int w, int h); | |||
void setExtendedDesktopSize(unsigned reason, unsigned result, | |||
@@ -81,8 +79,7 @@ private: | |||
void resizeFramebuffer(); | |||
void autoSelectFormatAndEncoding(); | |||
void checkEncodings(); | |||
void requestNewUpdate(); | |||
void updatePixelFormat(); | |||
static void handleOptions(void *data); | |||
@@ -101,21 +98,7 @@ private: | |||
rfb::PixelFormat serverPF; | |||
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 |
@@ -235,7 +235,7 @@ void DesktopWindow::setName(const char *name) | |||
void DesktopWindow::updateWindow() | |||
{ | |||
if (firstUpdate) { | |||
if (cc->cp.supportsSetDesktopSize) { | |||
if (cc->server.supportsSetDesktopSize) { | |||
// Hack: Wait until we're in the proper mode and position until | |||
// resizing things, otherwise we might send the wrong thing. | |||
if (delayedFullscreen) | |||
@@ -487,7 +487,7 @@ void DesktopWindow::resize(int x, int y, int w, int h) | |||
// d) We're not still waiting for startup fullscreen to kick in | |||
// | |||
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 | |||
// of resize events as the user is dragging the window. | |||
Fl::remove_timeout(handleResizeTimeout, this); | |||
@@ -1014,14 +1014,14 @@ void DesktopWindow::handleResizeTimeout(void *data) | |||
void DesktopWindow::remoteResize(int width, int height) | |||
{ | |||
ScreenSet layout; | |||
ScreenSet::iterator iter; | |||
ScreenSet::const_iterator iter; | |||
if (!fullscreen_active() || (width > w()) || (height > h())) { | |||
// In windowed mode (or the framebuffer is so large that we need | |||
// to scroll) we just report a single virtual screen that covers | |||
// 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 | |||
// safe as there is nothing to conflict with... | |||
@@ -1077,8 +1077,8 @@ void DesktopWindow::remoteResize(int width, int height) | |||
sy -= viewport_rect.tl.y; | |||
// 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) && | |||
(iter->dimensions.tl.y == sy) && | |||
(iter->dimensions.width() == sw) && | |||
@@ -1087,7 +1087,7 @@ void DesktopWindow::remoteResize(int width, int height) | |||
} | |||
// Found it? | |||
if (iter != cc->cp.screenLayout.end()) { | |||
if (iter != cc->server.screenLayout().end()) { | |||
layout.add_screen(*iter); | |||
continue; | |||
} | |||
@@ -1095,13 +1095,13 @@ void DesktopWindow::remoteResize(int width, int height) | |||
// Need to add a new one, which means we need to find an unused id | |||
while (true) { | |||
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) | |||
break; | |||
} | |||
if (iter == cc->cp.screenLayout.end()) | |||
if (iter == cc->server.screenLayout().end()) | |||
break; | |||
} | |||
@@ -1115,14 +1115,14 @@ void DesktopWindow::remoteResize(int width, int height) | |||
} | |||
// 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; | |||
char buffer[2048]; | |||
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)); | |||
vlog.debug("%s", buffer); | |||
@@ -425,7 +425,7 @@ void Viewport::pushLEDState() | |||
unsigned int state; | |||
// Server support? | |||
if (cc->cp.ledState() == ledUnknown) | |||
if (cc->server.ledState() == ledUnknown) | |||
return; | |||
state = 0; | |||
@@ -458,7 +458,7 @@ void Viewport::pushLEDState() | |||
state |= ledNumLock; | |||
// No support for Scroll Lock // | |||
state |= (cc->cp.ledState() & ledScrollLock); | |||
state |= (cc->server.ledState() & ledScrollLock); | |||
#else | |||
unsigned int mask; | |||
@@ -484,17 +484,17 @@ void Viewport::pushLEDState() | |||
state |= ledScrollLock; | |||
#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"); | |||
handleKeyPress(0x3a, XK_Caps_Lock); | |||
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"); | |||
handleKeyPress(0x45, XK_Num_Lock); | |||
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"); | |||
handleKeyPress(0x46, XK_Scroll_Lock); | |||
handleKeyRelease(0x46); |