@@ -19,10 +19,7 @@ if(BUILD_STATIC) | |||
set(BUILD_STATIC_GCC 1) | |||
set(JPEG_LIBRARIES "-Wl,-Bstatic -ljpeg -Wl,-Bdynamic") | |||
if(WIN32) | |||
set(ZLIB_LIBRARIES "-Wl,-Bstatic -lz -Wl,-Bdynamic") | |||
endif() | |||
set(ZLIB_LIBRARIES "-Wl,-Bstatic -lz -Wl,-Bdynamic") | |||
# gettext is included in libc on many unix systems | |||
if(NOT LIBC_HAS_DGETTEXT) |
@@ -49,7 +49,8 @@ CConnection::CConnection() | |||
CConnection::~CConnection() | |||
{ | |||
setFramebuffer(NULL); | |||
if (csecurity) csecurity->destroy(); | |||
if (csecurity) | |||
delete csecurity; | |||
delete reader_; | |||
reader_ = 0; | |||
delete writer_; | |||
@@ -234,14 +235,14 @@ void CConnection::processSecurityTypesMsg() | |||
} | |||
state_ = RFBSTATE_SECURITY; | |||
csecurity = security.GetCSecurity(secType); | |||
csecurity = security.GetCSecurity(this, secType); | |||
processSecurityMsg(); | |||
} | |||
void CConnection::processSecurityMsg() | |||
{ | |||
vlog.debug("processing security message"); | |||
if (csecurity->processMsg(this)) { | |||
if (csecurity->processMsg()) { | |||
state_ = RFBSTATE_SECURITY_RESULT; | |||
processSecurityResultMsg(); | |||
} |
@@ -44,9 +44,9 @@ namespace rfb { | |||
class CConnection; | |||
class CSecurity { | |||
public: | |||
CSecurity(CConnection* cc) { this->cc = cc; } | |||
virtual ~CSecurity() {} | |||
virtual bool processMsg(CConnection* cc)=0; | |||
virtual void destroy() { delete this; } | |||
virtual bool processMsg() = 0; | |||
virtual int getType() const = 0; | |||
virtual const char* description() const = 0; | |||
virtual bool isSecure() const { return false; } | |||
@@ -56,6 +56,9 @@ namespace rfb { | |||
* It MUST be set by viewer. | |||
*/ | |||
static UserPasswdGetter *upg; | |||
protected: | |||
CConnection* cc; | |||
}; | |||
} | |||
#endif |
@@ -29,7 +29,8 @@ namespace rfb { | |||
class CSecurityNone : public CSecurity { | |||
public: | |||
virtual bool processMsg(CConnection* cc) { return true; } | |||
CSecurityNone(CConnection* cc) : CSecurity(cc) {} | |||
virtual bool processMsg() { return true; } | |||
virtual int getType() const {return secTypeNone;} | |||
virtual const char* description() const {return "No Encryption";} | |||
}; |
@@ -26,7 +26,7 @@ | |||
using namespace rfb; | |||
bool CSecurityPlain::processMsg(CConnection* cc) | |||
bool CSecurityPlain::processMsg() | |||
{ | |||
rdr::OutStream* os = cc->getOutStream(); | |||
@@ -26,8 +26,8 @@ namespace rfb { | |||
class CSecurityPlain : public CSecurity { | |||
public: | |||
CSecurityPlain() {} | |||
virtual bool processMsg(CConnection* cc); | |||
CSecurityPlain(CConnection* cc) : CSecurity(cc) {} | |||
virtual bool processMsg(); | |||
virtual int getType() const { return secTypePlain; } | |||
virtual const char* description() const { return "ask for username and password"; } | |||
}; |
@@ -21,9 +21,9 @@ | |||
using namespace rfb; | |||
CSecurityStack::CSecurityStack(int Type, const char*Name, CSecurity* s0, | |||
CSecurity* s1) | |||
:name(Name),type(Type) | |||
CSecurityStack::CSecurityStack(CConnection* cc, int Type, const char* Name, | |||
CSecurity* s0, CSecurity* s1) | |||
: CSecurity(cc), name(Name), type(Type) | |||
{ | |||
state = 0; | |||
state0 = s0; | |||
@@ -38,12 +38,12 @@ CSecurityStack::~CSecurityStack() | |||
delete state1; | |||
} | |||
bool CSecurityStack::processMsg(CConnection* cc) | |||
bool CSecurityStack::processMsg() | |||
{ | |||
bool res=true; | |||
if (state == 0) { | |||
if (state0) | |||
res = state0->processMsg(cc); | |||
res = state0->processMsg(); | |||
if (!res) | |||
return res; | |||
@@ -53,7 +53,7 @@ bool CSecurityStack::processMsg(CConnection* cc) | |||
if (state == 1) { | |||
if(state1) | |||
res = state1->processMsg(cc); | |||
res = state1->processMsg(); | |||
if(!res) | |||
return res; |
@@ -27,9 +27,10 @@ namespace rfb { | |||
class CSecurityStack : public CSecurity { | |||
public: | |||
CSecurityStack(int Type, const char *Name, CSecurity* s0 = 0, CSecurity* s1 = 0); | |||
CSecurityStack(CConnection* cc, int Type, const char *Name, | |||
CSecurity* s0 = NULL, CSecurity* s1 = NULL); | |||
~CSecurityStack(); | |||
virtual bool processMsg(CConnection* cc); | |||
virtual bool processMsg(); | |||
virtual int getType() const {return type;}; | |||
virtual const char* description() const {return name;} | |||
virtual bool isSecure() const; |
@@ -67,8 +67,9 @@ StringParameter CSecurityTLS::X509CRL("X509CRL", "X509 CRL file", "", ConfViewer | |||
static LogWriter vlog("TLS"); | |||
CSecurityTLS::CSecurityTLS(bool _anon) : session(0), anon_cred(0), | |||
anon(_anon), fis(0), fos(0) | |||
CSecurityTLS::CSecurityTLS(CConnection* cc, bool _anon) | |||
: CSecurity(cc), session(NULL), anon_cred(NULL), cert_cred(NULL), | |||
anon(_anon), tlsis(NULL), tlsos(NULL), rawis(NULL), rawos(NULL) | |||
{ | |||
cafile = X509CA.getData(); | |||
crlfile = X509CRL.getData(); | |||
@@ -115,6 +116,21 @@ void CSecurityTLS::shutdown(bool needbye) | |||
cert_cred = 0; | |||
} | |||
if (rawis && rawos) { | |||
cc->setStreams(rawis, rawos); | |||
rawis = NULL; | |||
rawos = NULL; | |||
} | |||
if (tlsis) { | |||
delete tlsis; | |||
tlsis = NULL; | |||
} | |||
if (tlsos) { | |||
delete tlsos; | |||
tlsos = NULL; | |||
} | |||
if (session) { | |||
gnutls_deinit(session); | |||
session = 0; | |||
@@ -126,18 +142,13 @@ CSecurityTLS::~CSecurityTLS() | |||
{ | |||
shutdown(true); | |||
if (fis) | |||
delete fis; | |||
if (fos) | |||
delete fos; | |||
delete[] cafile; | |||
delete[] crlfile; | |||
gnutls_global_deinit(); | |||
} | |||
bool CSecurityTLS::processMsg(CConnection* cc) | |||
bool CSecurityTLS::processMsg() | |||
{ | |||
rdr::InStream* is = cc->getInStream(); | |||
rdr::OutStream* os = cc->getOutStream(); | |||
@@ -164,17 +175,19 @@ bool CSecurityTLS::processMsg(CConnection* cc) | |||
throw AuthFailureException("gnutls_set_default_priority failed"); | |||
setParam(); | |||
} | |||
rdr::TLSInStream *tlsis = new rdr::TLSInStream(is, session); | |||
rdr::TLSOutStream *tlsos = new rdr::TLSOutStream(os, session); | |||
// Create these early as they set up the push/pull functions | |||
// for GnuTLS | |||
tlsis = new rdr::TLSInStream(is, session); | |||
tlsos = new rdr::TLSOutStream(os, session); | |||
rawis = is; | |||
rawos = os; | |||
} | |||
int err; | |||
err = gnutls_handshake(session); | |||
if (err != GNUTLS_E_SUCCESS) { | |||
delete tlsis; | |||
delete tlsos; | |||
if (!gnutls_error_is_fatal(err)) | |||
return false; | |||
@@ -185,7 +198,7 @@ bool CSecurityTLS::processMsg(CConnection* cc) | |||
checkSession(); | |||
cc->setStreams(fis = tlsis, fos = tlsos); | |||
cc->setStreams(tlsis, tlsos); | |||
return true; | |||
} |
@@ -42,9 +42,9 @@ namespace rfb { | |||
class UserMsgBox; | |||
class CSecurityTLS : public CSecurity { | |||
public: | |||
CSecurityTLS(bool _anon); | |||
CSecurityTLS(CConnection* cc, bool _anon); | |||
virtual ~CSecurityTLS(); | |||
virtual bool processMsg(CConnection* cc); | |||
virtual bool processMsg(); | |||
virtual int getType() const { return anon ? secTypeTLSNone : secTypeX509None; } | |||
virtual const char* description() const | |||
{ return anon ? "TLS Encryption without VncAuth" : "X509 Encryption without VncAuth"; } | |||
@@ -69,8 +69,12 @@ namespace rfb { | |||
bool anon; | |||
char *cafile, *crlfile; | |||
rdr::InStream* fis; | |||
rdr::OutStream* fos; | |||
rdr::InStream* tlsis; | |||
rdr::OutStream* tlsos; | |||
rdr::InStream* rawis; | |||
rdr::OutStream* rawos; | |||
}; | |||
} | |||
@@ -36,7 +36,8 @@ using namespace std; | |||
static LogWriter vlog("CVeNCrypt"); | |||
CSecurityVeNCrypt::CSecurityVeNCrypt(SecurityClient* sec) : csecurity(NULL), security(sec) | |||
CSecurityVeNCrypt::CSecurityVeNCrypt(CConnection* cc, SecurityClient* sec) | |||
: CSecurity(cc), csecurity(NULL), security(sec) | |||
{ | |||
haveRecvdMajorVersion = false; | |||
haveRecvdMinorVersion = false; | |||
@@ -59,7 +60,7 @@ CSecurityVeNCrypt::~CSecurityVeNCrypt() | |||
delete[] availableTypes; | |||
} | |||
bool CSecurityVeNCrypt::processMsg(CConnection* cc) | |||
bool CSecurityVeNCrypt::processMsg() | |||
{ | |||
InStream* is = cc->getInStream(); | |||
OutStream* os = cc->getOutStream(); | |||
@@ -171,7 +172,7 @@ bool CSecurityVeNCrypt::processMsg(CConnection* cc) | |||
if (chosenType == secTypeInvalid || chosenType == secTypeVeNCrypt) | |||
throw AuthFailureException("No valid VeNCrypt sub-type"); | |||
csecurity = security->GetCSecurity(chosenType); | |||
csecurity = security->GetCSecurity(cc, chosenType); | |||
/* send chosen type to server */ | |||
os->writeU32(chosenType); | |||
@@ -188,7 +189,7 @@ bool CSecurityVeNCrypt::processMsg(CConnection* cc) | |||
throw AuthFailureException("The server reported 0 VeNCrypt sub-types"); | |||
} | |||
return csecurity->processMsg(cc); | |||
return csecurity->processMsg(); | |||
} | |||
const char* CSecurityVeNCrypt::description() const |
@@ -34,9 +34,9 @@ namespace rfb { | |||
class CSecurityVeNCrypt : public CSecurity { | |||
public: | |||
CSecurityVeNCrypt(SecurityClient* sec); | |||
CSecurityVeNCrypt(CConnection* cc, SecurityClient* sec); | |||
~CSecurityVeNCrypt(); | |||
virtual bool processMsg(CConnection* cc);// { return true; } | |||
virtual bool processMsg(); | |||
int getType() const {return chosenType;} | |||
virtual const char* description() const; | |||
virtual bool isSecure() const; |
@@ -40,7 +40,7 @@ using namespace rfb; | |||
static const int vncAuthChallengeSize = 16; | |||
bool CSecurityVncAuth::processMsg(CConnection* cc) | |||
bool CSecurityVncAuth::processMsg() | |||
{ | |||
rdr::InStream* is = cc->getInStream(); | |||
rdr::OutStream* os = cc->getOutStream(); |
@@ -25,9 +25,9 @@ namespace rfb { | |||
class CSecurityVncAuth : public CSecurity { | |||
public: | |||
CSecurityVncAuth(void) {} | |||
CSecurityVncAuth(CConnection* cc) : CSecurity(cc) {} | |||
virtual ~CSecurityVncAuth() {} | |||
virtual bool processMsg(CConnection* cc); | |||
virtual bool processMsg(); | |||
virtual int getType() const {return secTypeVncAuth;}; | |||
virtual const char* description() const {return "No Encryption";} | |||
}; |
@@ -291,11 +291,20 @@ int Congestion::getUncongestedETA() | |||
size_t Congestion::getBandwidth() | |||
{ | |||
size_t bandwidth; | |||
// No measurements yet? Guess RTT of 60 ms | |||
if (safeBaseRTT == (unsigned)-1) | |||
return congWindow * 1000 / 60; | |||
bandwidth = congWindow * 1000 / 60; | |||
else | |||
bandwidth = congWindow * 1000 / safeBaseRTT; | |||
// We're still probing so guess actual bandwidth is halfway between | |||
// the current guess and the next one (slow start doubles each time) | |||
if (inSlowStart) | |||
bandwidth = bandwidth + bandwidth / 2; | |||
return congWindow * 1000 / safeBaseRTT; | |||
return bandwidth; | |||
} | |||
void Congestion::debugTrace(const char* filename, int fd) |
@@ -1,6 +1,7 @@ | |||
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved. | |||
* Copyright (C) 2011 D. R. Commander. All Rights Reserved. | |||
* Copyright 2014-2018 Pierre Ossman for Cendio AB | |||
* Copyright 2018 Peter Astrand 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 | |||
@@ -50,6 +51,9 @@ static const int SolidSearchBlock = 16; | |||
// Don't bother with blocks smaller than this | |||
static const int SolidBlockMinArea = 2048; | |||
// How long we consider a region recently changed (in ms) | |||
static const int RecentChangeTimeout = 50; | |||
namespace rfb { | |||
enum EncoderClass { | |||
@@ -123,7 +127,8 @@ static const char *encoderTypeName(EncoderType type) | |||
return "Unknown Encoder Type"; | |||
} | |||
EncodeManager::EncodeManager(SConnection* conn_) : conn(conn_) | |||
EncodeManager::EncodeManager(SConnection* conn_) | |||
: conn(conn_), recentChangeTimer(this) | |||
{ | |||
StatsVector::iterator iter; | |||
@@ -253,23 +258,57 @@ bool EncodeManager::needsLosslessRefresh(const Region& req) | |||
return !lossyRegion.intersect(req).is_empty(); | |||
} | |||
int EncodeManager::getNextLosslessRefresh(const Region& req) | |||
{ | |||
// Do we have something we can send right away? | |||
if (!pendingRefreshRegion.intersect(req).is_empty()) | |||
return 0; | |||
assert(needsLosslessRefresh(req)); | |||
assert(recentChangeTimer.isStarted()); | |||
return recentChangeTimer.getNextTimeout(); | |||
} | |||
void EncodeManager::pruneLosslessRefresh(const Region& limits) | |||
{ | |||
lossyRegion.assign_intersect(limits); | |||
pendingRefreshRegion.assign_intersect(limits); | |||
} | |||
void EncodeManager::writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb, | |||
const RenderedCursor* renderedCursor) | |||
{ | |||
doUpdate(true, ui.changed, ui.copied, ui.copy_delta, pb, renderedCursor); | |||
doUpdate(true, ui.changed, ui.copied, ui.copy_delta, pb, renderedCursor); | |||
recentlyChangedRegion.assign_union(ui.changed); | |||
recentlyChangedRegion.assign_union(ui.copied); | |||
if (!recentChangeTimer.isStarted()) | |||
recentChangeTimer.start(RecentChangeTimeout); | |||
} | |||
void EncodeManager::writeLosslessRefresh(const Region& req, const PixelBuffer* pb, | |||
const RenderedCursor* renderedCursor, | |||
size_t maxUpdateSize) | |||
{ | |||
doUpdate(false, getLosslessRefresh(req, maxUpdateSize), | |||
Region(), Point(), pb, renderedCursor); | |||
doUpdate(false, getLosslessRefresh(req, maxUpdateSize), | |||
Region(), Point(), pb, renderedCursor); | |||
} | |||
bool EncodeManager::handleTimeout(Timer* t) | |||
{ | |||
if (t == &recentChangeTimer) { | |||
// Any lossy region that wasn't recently updated can | |||
// now be scheduled for a refresh | |||
pendingRefreshRegion.assign_union(lossyRegion.subtract(recentlyChangedRegion)); | |||
recentlyChangedRegion.clear(); | |||
// Will there be more to do? (i.e. do we need another round) | |||
if (!lossyRegion.subtract(pendingRefreshRegion).is_empty()) | |||
return true; | |||
} | |||
return false; | |||
} | |||
void EncodeManager::doUpdate(bool allowLossy, const Region& changed_, | |||
@@ -325,6 +364,8 @@ void EncodeManager::prepareEncoders(bool allowLossy) | |||
enum EncoderClass solid, bitmap, bitmapRLE; | |||
enum EncoderClass indexed, indexedRLE, fullColour; | |||
bool allowJPEG; | |||
rdr::S32 preferred; | |||
std::vector<int>::iterator iter; | |||
@@ -332,6 +373,12 @@ void EncodeManager::prepareEncoders(bool allowLossy) | |||
solid = bitmap = bitmapRLE = encoderRaw; | |||
indexed = indexedRLE = fullColour = encoderRaw; | |||
allowJPEG = conn->cp.pf().bpp >= 16; | |||
if (!allowLossy) { | |||
if (encoders[encoderTightJPEG]->losslessQuality == -1) | |||
allowJPEG = false; | |||
} | |||
// Try to respect the client's wishes | |||
preferred = conn->getPreferredEncoding(); | |||
switch (preferred) { | |||
@@ -344,8 +391,7 @@ void EncodeManager::prepareEncoders(bool allowLossy) | |||
bitmapRLE = indexedRLE = fullColour = encoderHextile; | |||
break; | |||
case encodingTight: | |||
if (encoders[encoderTightJPEG]->isSupported() && | |||
(conn->cp.pf().bpp >= 16) && allowLossy) | |||
if (encoders[encoderTightJPEG]->isSupported() && allowJPEG) | |||
fullColour = encoderTightJPEG; | |||
else | |||
fullColour = encoderTight; | |||
@@ -362,8 +408,7 @@ void EncodeManager::prepareEncoders(bool allowLossy) | |||
// Any encoders still unassigned? | |||
if (fullColour == encoderRaw) { | |||
if (encoders[encoderTightJPEG]->isSupported() && | |||
(conn->cp.pf().bpp >= 16) && allowLossy) | |||
if (encoders[encoderTightJPEG]->isSupported() && allowJPEG) | |||
fullColour = encoderTightJPEG; | |||
else if (encoders[encoderZRLE]->isSupported()) | |||
fullColour = encoderZRLE; | |||
@@ -421,9 +466,17 @@ void EncodeManager::prepareEncoders(bool allowLossy) | |||
encoder = encoders[*iter]; | |||
encoder->setCompressLevel(conn->cp.compressLevel); | |||
encoder->setQualityLevel(conn->cp.qualityLevel); | |||
encoder->setFineQualityLevel(conn->cp.fineQualityLevel, | |||
conn->cp.subsampling); | |||
if (allowLossy) { | |||
encoder->setQualityLevel(conn->cp.qualityLevel); | |||
encoder->setFineQualityLevel(conn->cp.fineQualityLevel, | |||
conn->cp.subsampling); | |||
} else { | |||
int level = __rfbmax(conn->cp.qualityLevel, | |||
encoder->losslessQuality); | |||
encoder->setQualityLevel(level); | |||
encoder->setFineQualityLevel(-1, subsampleUndefined); | |||
} | |||
} | |||
} | |||
@@ -437,8 +490,11 @@ Region EncodeManager::getLosslessRefresh(const Region& req, | |||
// We make a conservative guess at the compression ratio at 2:1 | |||
maxUpdateSize *= 2; | |||
// We will measure pixels, not bytes (assume 32 bpp) | |||
maxUpdateSize /= 4; | |||
area = 0; | |||
lossyRegion.intersect(req).get_rects(&rects); | |||
pendingRefreshRegion.intersect(req).get_rects(&rects); | |||
while (!rects.empty()) { | |||
size_t idx; | |||
Rect rect; | |||
@@ -525,11 +581,17 @@ Encoder *EncodeManager::startRect(const Rect& rect, int type) | |||
encoder = encoders[klass]; | |||
conn->writer()->startRect(rect, encoder->encoding); | |||
if (encoder->flags & EncoderLossy) | |||
if ((encoder->flags & EncoderLossy) && | |||
((encoder->losslessQuality == -1) || | |||
(encoder->getQualityLevel() < encoder->losslessQuality))) | |||
lossyRegion.assign_union(Region(rect)); | |||
else | |||
lossyRegion.assign_subtract(Region(rect)); | |||
// This was either a rect getting refreshed, or a rect that just got | |||
// new content. Either way we should not try to refresh it anymore. | |||
pendingRefreshRegion.assign_subtract(Region(rect)); | |||
return encoder; | |||
} | |||
@@ -574,6 +636,10 @@ void EncodeManager::writeCopyRects(const Region& copied, const Point& delta) | |||
lossyCopy.translate(delta); | |||
lossyCopy.assign_intersect(copied); | |||
lossyRegion.assign_union(lossyCopy); | |||
// Stop any pending refresh as a copy is enough that we consider | |||
// this region to be recently changed | |||
pendingRefreshRegion.assign_subtract(copied); | |||
} | |||
void EncodeManager::writeSolidRects(Region *changed, const PixelBuffer* pb) |
@@ -25,6 +25,7 @@ | |||
#include <rdr/types.h> | |||
#include <rfb/PixelBuffer.h> | |||
#include <rfb/Region.h> | |||
#include <rfb/Timer.h> | |||
namespace rfb { | |||
class SConnection; | |||
@@ -36,7 +37,7 @@ namespace rfb { | |||
struct RectInfo; | |||
class EncodeManager { | |||
class EncodeManager : public Timer::Callback { | |||
public: | |||
EncodeManager(SConnection* conn); | |||
~EncodeManager(); | |||
@@ -47,6 +48,8 @@ namespace rfb { | |||
static bool supported(int encoding); | |||
bool needsLosslessRefresh(const Region& req); | |||
int getNextLosslessRefresh(const Region& req); | |||
void pruneLosslessRefresh(const Region& limits); | |||
void writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb, | |||
@@ -57,6 +60,8 @@ namespace rfb { | |||
size_t maxUpdateSize); | |||
protected: | |||
virtual bool handleTimeout(Timer* t); | |||
void doUpdate(bool allowLossy, const Region& changed, | |||
const Region& copied, const Point& copy_delta, | |||
const PixelBuffer* pb, | |||
@@ -117,6 +122,10 @@ namespace rfb { | |||
std::vector<int> activeEncoders; | |||
Region lossyRegion; | |||
Region recentlyChangedRegion; | |||
Region pendingRefreshRegion; | |||
Timer recentChangeTimer; | |||
struct EncoderStats { | |||
unsigned rects; |
@@ -24,9 +24,11 @@ | |||
using namespace rfb; | |||
Encoder::Encoder(SConnection *conn_, int encoding_, | |||
enum EncoderFlags flags_, unsigned int maxPaletteSize_) : | |||
enum EncoderFlags flags_, | |||
unsigned int maxPaletteSize_, int losslessQuality_) : | |||
encoding(encoding_), flags(flags_), | |||
maxPaletteSize(maxPaletteSize_), conn(conn_) | |||
maxPaletteSize(maxPaletteSize_), losslessQuality(losslessQuality_), | |||
conn(conn_) | |||
{ | |||
} | |||
@@ -42,7 +42,8 @@ namespace rfb { | |||
class Encoder { | |||
public: | |||
Encoder(SConnection* conn, int encoding, | |||
enum EncoderFlags flags, unsigned int maxPaletteSize); | |||
enum EncoderFlags flags, unsigned int maxPaletteSize=-1, | |||
int losslessQuality=-1); | |||
virtual ~Encoder(); | |||
// isSupported() should return a boolean indicating if this encoder | |||
@@ -54,6 +55,9 @@ namespace rfb { | |||
virtual void setQualityLevel(int level) {}; | |||
virtual void setFineQualityLevel(int quality, int subsampling) {}; | |||
virtual int getCompressLevel() { return -1; }; | |||
virtual int getQualityLevel() { return -1; }; | |||
// writeRect() is the main interface that encodes the given rectangle | |||
// with data from the PixelBuffer onto the SConnection given at | |||
// encoder creation. | |||
@@ -92,6 +96,10 @@ namespace rfb { | |||
// Maximum size of the palette per rect | |||
const unsigned int maxPaletteSize; | |||
// Minimum level where the quality loss will not be noticed by | |||
// most users (only relevant with EncoderLossy flag) | |||
const int losslessQuality; | |||
protected: | |||
SConnection* conn; | |||
}; |
@@ -45,7 +45,7 @@ BoolParameter improvedHextile("ImprovedHextile", | |||
#undef BPP | |||
HextileEncoder::HextileEncoder(SConnection* conn) : | |||
Encoder(conn, encodingHextile, EncoderPlain, -1) | |||
Encoder(conn, encodingHextile, EncoderPlain) | |||
{ | |||
} | |||
@@ -37,7 +37,7 @@ using namespace rfb; | |||
#undef BPP | |||
RREEncoder::RREEncoder(SConnection* conn) : | |||
Encoder(conn, encodingRRE, EncoderPlain, -1) | |||
Encoder(conn, encodingRRE, EncoderPlain) | |||
{ | |||
} | |||
@@ -25,7 +25,7 @@ | |||
using namespace rfb; | |||
RawEncoder::RawEncoder(SConnection* conn) : | |||
Encoder(conn, encodingRaw, EncoderPlain, -1) | |||
Encoder(conn, encodingRaw, EncoderPlain) | |||
{ | |||
} | |||
@@ -64,7 +64,8 @@ SConnection::SConnection() | |||
SConnection::~SConnection() | |||
{ | |||
if (ssecurity) ssecurity->destroy(); | |||
if (ssecurity) | |||
delete ssecurity; | |||
delete reader_; | |||
reader_ = 0; | |||
delete writer_; | |||
@@ -155,7 +156,7 @@ void SConnection::processVersionMsg() | |||
os->writeU32(*i); | |||
if (*i == secTypeNone) os->flush(); | |||
state_ = RFBSTATE_SECURITY; | |||
ssecurity = security.GetSSecurity(*i); | |||
ssecurity = security.GetSSecurity(this, *i); | |||
processSecurityMsg(); | |||
return; | |||
} | |||
@@ -198,7 +199,7 @@ void SConnection::processSecurityType(int secType) | |||
try { | |||
state_ = RFBSTATE_SECURITY; | |||
ssecurity = security.GetSSecurity(secType); | |||
ssecurity = security.GetSSecurity(this, secType); | |||
} catch (rdr::Exception& e) { | |||
throwConnFailedException("%s", e.str()); | |||
} | |||
@@ -210,7 +211,7 @@ void SConnection::processSecurityMsg() | |||
{ | |||
vlog.debug("processing security message"); | |||
try { | |||
bool done = ssecurity->processMsg(this); | |||
bool done = ssecurity->processMsg(); | |||
if (done) { | |||
state_ = RFBSTATE_QUERYING; | |||
setAccessRights(ssecurity->getAccessRights()); |
@@ -52,9 +52,9 @@ namespace rfb { | |||
class SSecurity { | |||
public: | |||
SSecurity(SConnection* sc) { this->sc = sc; } | |||
virtual ~SSecurity() {} | |||
virtual bool processMsg(SConnection* sc)=0; | |||
virtual void destroy() { delete this; } | |||
virtual bool processMsg() = 0; | |||
virtual int getType() const = 0; | |||
// getUserName() gets the name of the user attempting authentication. The | |||
@@ -64,6 +64,9 @@ namespace rfb { | |||
virtual const char* getUserName() const = 0; | |||
virtual SConnection::AccessRights getAccessRights() const { return SConnection::AccessDefault; } | |||
protected: | |||
SConnection* sc; | |||
}; | |||
} |
@@ -28,7 +28,8 @@ namespace rfb { | |||
class SSecurityNone : public SSecurity { | |||
public: | |||
virtual bool processMsg(SConnection* sc) { return true; } | |||
SSecurityNone(SConnection* sc) : SSecurity(sc) {} | |||
virtual bool processMsg() { return true; } | |||
virtual int getType() const {return secTypeNone;} | |||
virtual const char* getUserName() const {return 0;} | |||
}; |
@@ -60,7 +60,7 @@ bool PasswordValidator::validUser(const char* username) | |||
return false; | |||
} | |||
SSecurityPlain::SSecurityPlain() | |||
SSecurityPlain::SSecurityPlain(SConnection* sc) : SSecurity(sc) | |||
{ | |||
#ifdef HAVE_PAM | |||
valid = new UnixPasswordValidator(); | |||
@@ -73,7 +73,7 @@ SSecurityPlain::SSecurityPlain() | |||
state = 0; | |||
} | |||
bool SSecurityPlain::processMsg(SConnection* sc) | |||
bool SSecurityPlain::processMsg() | |||
{ | |||
rdr::InStream* is = sc->getInStream(); | |||
char* pw; |
@@ -47,8 +47,8 @@ namespace rfb { | |||
class SSecurityPlain : public SSecurity { | |||
public: | |||
SSecurityPlain(); | |||
virtual bool processMsg(SConnection* sc); | |||
SSecurityPlain(SConnection* sc); | |||
virtual bool processMsg(); | |||
virtual int getType() const { return secTypePlain; }; | |||
virtual const char* getUserName() const { return username.buf; } | |||
@@ -20,8 +20,11 @@ | |||
using namespace rfb; | |||
SSecurityStack::SSecurityStack(int Type, SSecurity* s0, SSecurity* s1) | |||
:state(0), state0(s0), state1(s1), type(Type) {} | |||
SSecurityStack::SSecurityStack(SConnection* sc, int Type, | |||
SSecurity* s0, SSecurity* s1) | |||
: SSecurity(sc), state(0), state0(s0), state1(s1), type(Type) | |||
{ | |||
} | |||
SSecurityStack::~SSecurityStack() | |||
{ | |||
@@ -31,13 +34,13 @@ SSecurityStack::~SSecurityStack() | |||
delete state1; | |||
} | |||
bool SSecurityStack::processMsg(SConnection* cc) | |||
bool SSecurityStack::processMsg() | |||
{ | |||
bool res = true; | |||
if (state == 0) { | |||
if (state0) | |||
res = state0->processMsg(cc); | |||
res = state0->processMsg(); | |||
if (!res) | |||
return res; | |||
state++; | |||
@@ -45,7 +48,7 @@ bool SSecurityStack::processMsg(SConnection* cc) | |||
if (state == 1) { | |||
if (state1) | |||
res = state1->processMsg(cc); | |||
res = state1->processMsg(); | |||
if (!res) | |||
return res; | |||
state++; |
@@ -26,9 +26,10 @@ namespace rfb { | |||
class SSecurityStack : public SSecurity { | |||
public: | |||
SSecurityStack(int Type, SSecurity* s0 = 0, SSecurity* s1 = 0); | |||
SSecurityStack(SConnection* sc, int Type, | |||
SSecurity* s0 = NULL, SSecurity* s1 = NULL); | |||
~SSecurityStack(); | |||
virtual bool processMsg(SConnection* cc); | |||
virtual bool processMsg(); | |||
virtual int getType() const { return type; }; | |||
virtual const char* getUserName() const; | |||
virtual SConnection::AccessRights getAccessRights() const; |
@@ -49,9 +49,10 @@ StringParameter SSecurityTLS::X509_KeyFile | |||
static LogWriter vlog("TLS"); | |||
SSecurityTLS::SSecurityTLS(bool _anon) : session(0), dh_params(0), | |||
anon_cred(0), cert_cred(0), | |||
anon(_anon), fis(0), fos(0) | |||
SSecurityTLS::SSecurityTLS(SConnection* sc, bool _anon) | |||
: SSecurity(sc), session(NULL), dh_params(NULL), anon_cred(NULL), | |||
cert_cred(NULL), anon(_anon), tlsis(NULL), tlsos(NULL), | |||
rawis(NULL), rawos(NULL) | |||
{ | |||
certfile = X509_CertFile.getData(); | |||
keyfile = X509_KeyFile.getData(); | |||
@@ -84,6 +85,21 @@ void SSecurityTLS::shutdown() | |||
cert_cred = 0; | |||
} | |||
if (rawis && rawos) { | |||
sc->setStreams(rawis, rawos); | |||
rawis = NULL; | |||
rawos = NULL; | |||
} | |||
if (tlsis) { | |||
delete tlsis; | |||
tlsis = NULL; | |||
} | |||
if (tlsos) { | |||
delete tlsos; | |||
tlsos = NULL; | |||
} | |||
if (session) { | |||
gnutls_deinit(session); | |||
session = 0; | |||
@@ -95,25 +111,20 @@ SSecurityTLS::~SSecurityTLS() | |||
{ | |||
shutdown(); | |||
if (fis) | |||
delete fis; | |||
if (fos) | |||
delete fos; | |||
delete[] keyfile; | |||
delete[] certfile; | |||
gnutls_global_deinit(); | |||
} | |||
bool SSecurityTLS::processMsg(SConnection *sc) | |||
bool SSecurityTLS::processMsg() | |||
{ | |||
rdr::InStream* is = sc->getInStream(); | |||
rdr::OutStream* os = sc->getOutStream(); | |||
vlog.debug("Process security message (session %p)", session); | |||
if (!session) { | |||
rdr::InStream* is = sc->getInStream(); | |||
rdr::OutStream* os = sc->getOutStream(); | |||
if (gnutls_init(&session, GNUTLS_SERVER) != GNUTLS_E_SUCCESS) | |||
throw AuthFailureException("gnutls_init failed"); | |||
@@ -130,17 +141,19 @@ bool SSecurityTLS::processMsg(SConnection *sc) | |||
os->writeU8(1); | |||
os->flush(); | |||
} | |||
rdr::TLSInStream *tlsis = new rdr::TLSInStream(is, session); | |||
rdr::TLSOutStream *tlsos = new rdr::TLSOutStream(os, session); | |||
// Create these early as they set up the push/pull functions | |||
// for GnuTLS | |||
tlsis = new rdr::TLSInStream(is, session); | |||
tlsos = new rdr::TLSOutStream(os, session); | |||
rawis = is; | |||
rawos = os; | |||
} | |||
int err; | |||
err = gnutls_handshake(session); | |||
if (err != GNUTLS_E_SUCCESS) { | |||
delete tlsis; | |||
delete tlsos; | |||
if (!gnutls_error_is_fatal(err)) { | |||
vlog.debug("Deferring completion of TLS handshake: %s", gnutls_strerror(err)); | |||
return false; | |||
@@ -152,7 +165,7 @@ bool SSecurityTLS::processMsg(SConnection *sc) | |||
vlog.debug("Handshake completed"); | |||
sc->setStreams(fis = tlsis, fos = tlsos); | |||
sc->setStreams(tlsis, tlsos); | |||
return true; | |||
} |
@@ -40,9 +40,9 @@ namespace rfb { | |||
class SSecurityTLS : public SSecurity { | |||
public: | |||
SSecurityTLS(bool _anon); | |||
SSecurityTLS(SConnection* sc, bool _anon); | |||
virtual ~SSecurityTLS(); | |||
virtual bool processMsg(SConnection* sc); | |||
virtual bool processMsg(); | |||
virtual const char* getUserName() const {return 0;} | |||
virtual int getType() const { return anon ? secTypeTLSNone : secTypeX509None;} | |||
@@ -63,8 +63,11 @@ namespace rfb { | |||
int type; | |||
bool anon; | |||
rdr::InStream* fis; | |||
rdr::OutStream* fos; | |||
rdr::InStream* tlsis; | |||
rdr::OutStream* tlsos; | |||
rdr::InStream* rawis; | |||
rdr::OutStream* rawos; | |||
}; | |||
} |
@@ -38,7 +38,8 @@ using namespace std; | |||
static LogWriter vlog("SVeNCrypt"); | |||
SSecurityVeNCrypt::SSecurityVeNCrypt(SecurityServer *sec) : security(sec) | |||
SSecurityVeNCrypt::SSecurityVeNCrypt(SConnection* sc, SecurityServer *sec) | |||
: SSecurity(sc), security(sec) | |||
{ | |||
ssecurity = NULL; | |||
haveSentVersion = false; | |||
@@ -63,7 +64,7 @@ SSecurityVeNCrypt::~SSecurityVeNCrypt() | |||
} | |||
} | |||
bool SSecurityVeNCrypt::processMsg(SConnection* sc) | |||
bool SSecurityVeNCrypt::processMsg() | |||
{ | |||
rdr::InStream* is = sc->getInStream(); | |||
rdr::OutStream* os = sc->getOutStream(); | |||
@@ -166,11 +167,11 @@ bool SSecurityVeNCrypt::processMsg(SConnection* sc) | |||
if (chosenType == secTypeInvalid || chosenType == secTypeVeNCrypt) | |||
throw AuthFailureException("No valid VeNCrypt sub-type"); | |||
ssecurity = security->GetSSecurity(chosenType); | |||
ssecurity = security->GetSSecurity(sc, chosenType); | |||
} | |||
/* continue processing the messages */ | |||
return ssecurity->processMsg(sc); | |||
return ssecurity->processMsg(); | |||
} | |||
const char* SSecurityVeNCrypt::getUserName() const |
@@ -36,9 +36,9 @@ namespace rfb { | |||
class SSecurityVeNCrypt : public SSecurity { | |||
public: | |||
SSecurityVeNCrypt(SecurityServer *sec); | |||
SSecurityVeNCrypt(SConnection* sc, SecurityServer *sec); | |||
~SSecurityVeNCrypt(); | |||
virtual bool processMsg(SConnection* sc);// { return true; } | |||
virtual bool processMsg(); | |||
virtual int getType() const { return chosenType; } | |||
virtual const char* getUserName() const; | |||
virtual SConnection::AccessRights getAccessRights() const; |
@@ -48,8 +48,9 @@ VncAuthPasswdParameter SSecurityVncAuth::vncAuthPasswd | |||
("Password", "Obfuscated binary encoding of the password which clients must supply to " | |||
"access the server", &SSecurityVncAuth::vncAuthPasswdFile); | |||
SSecurityVncAuth::SSecurityVncAuth(void) | |||
: sentChallenge(false), responsePos(0), pg(&vncAuthPasswd), accessRights(0) | |||
SSecurityVncAuth::SSecurityVncAuth(SConnection* sc) | |||
: SSecurity(sc), sentChallenge(false), responsePos(0), | |||
pg(&vncAuthPasswd), accessRights(0) | |||
{ | |||
} | |||
@@ -70,7 +71,7 @@ bool SSecurityVncAuth::verifyResponse(const PlainPasswd &password) | |||
return memcmp(response, expectedResponse, vncAuthChallengeSize) == 0; | |||
} | |||
bool SSecurityVncAuth::processMsg(SConnection* sc) | |||
bool SSecurityVncAuth::processMsg() | |||
{ | |||
rdr::InStream* is = sc->getInStream(); | |||
rdr::OutStream* os = sc->getOutStream(); |
@@ -51,8 +51,8 @@ namespace rfb { | |||
class SSecurityVncAuth : public SSecurity { | |||
public: | |||
SSecurityVncAuth(void); | |||
virtual bool processMsg(SConnection* sc); | |||
SSecurityVncAuth(SConnection* sc); | |||
virtual bool processMsg(); | |||
virtual int getType() const {return secTypeVncAuth;} | |||
virtual const char* getUserName() const {return 0;} | |||
virtual SConnection::AccessRights getAccessRights() const { return accessRights; } |
@@ -55,7 +55,7 @@ StringParameter SecurityClient::secTypes | |||
#endif | |||
ConfViewer); | |||
CSecurity* SecurityClient::GetCSecurity(U32 secType) | |||
CSecurity* SecurityClient::GetCSecurity(CConnection* cc, U32 secType) | |||
{ | |||
assert (CSecurity::upg != NULL); /* (upg == NULL) means bug in the viewer */ | |||
#ifdef HAVE_GNUTLS | |||
@@ -66,29 +66,39 @@ CSecurity* SecurityClient::GetCSecurity(U32 secType) | |||
goto bail; | |||
switch (secType) { | |||
case secTypeNone: return new CSecurityNone(); | |||
case secTypeVncAuth: return new CSecurityVncAuth(); | |||
case secTypeVeNCrypt: return new CSecurityVeNCrypt(this); | |||
case secTypePlain: return new CSecurityPlain(); | |||
case secTypeNone: return new CSecurityNone(cc); | |||
case secTypeVncAuth: return new CSecurityVncAuth(cc); | |||
case secTypeVeNCrypt: return new CSecurityVeNCrypt(cc, this); | |||
case secTypePlain: return new CSecurityPlain(cc); | |||
#ifdef HAVE_GNUTLS | |||
case secTypeTLSNone: | |||
return new CSecurityStack(secTypeTLSNone, "TLS with no password", | |||
new CSecurityTLS(true)); | |||
return new CSecurityStack(cc, secTypeTLSNone, | |||
"TLS with no password", | |||
new CSecurityTLS(cc, true)); | |||
case secTypeTLSVnc: | |||
return new CSecurityStack(secTypeTLSVnc, "TLS with VNCAuth", | |||
new CSecurityTLS(true), new CSecurityVncAuth()); | |||
return new CSecurityStack(cc, secTypeTLSVnc, | |||
"TLS with VNCAuth", | |||
new CSecurityTLS(cc, true), | |||
new CSecurityVncAuth(cc)); | |||
case secTypeTLSPlain: | |||
return new CSecurityStack(secTypeTLSPlain, "TLS with Username/Password", | |||
new CSecurityTLS(true), new CSecurityPlain()); | |||
return new CSecurityStack(cc, secTypeTLSPlain, | |||
"TLS with Username/Password", | |||
new CSecurityTLS(cc, true), | |||
new CSecurityPlain(cc)); | |||
case secTypeX509None: | |||
return new CSecurityStack(secTypeX509None, "X509 with no password", | |||
new CSecurityTLS(false)); | |||
return new CSecurityStack(cc, secTypeX509None, | |||
"X509 with no password", | |||
new CSecurityTLS(cc, false)); | |||
case secTypeX509Vnc: | |||
return new CSecurityStack(secTypeX509Vnc, "X509 with VNCAuth", | |||
new CSecurityTLS(false), new CSecurityVncAuth()); | |||
return new CSecurityStack(cc, secTypeX509Vnc, | |||
"X509 with VNCAuth", | |||
new CSecurityTLS(cc, false), | |||
new CSecurityVncAuth(cc)); | |||
case secTypeX509Plain: | |||
return new CSecurityStack(secTypeX509Plain, "X509 with Username/Password", | |||
new CSecurityTLS(false), new CSecurityPlain()); | |||
return new CSecurityStack(cc, secTypeX509Plain, | |||
"X509 with Username/Password", | |||
new CSecurityTLS(cc, false), | |||
new CSecurityPlain(cc)); | |||
#endif | |||
} | |||
@@ -33,7 +33,7 @@ namespace rfb { | |||
SecurityClient(void) : Security(secTypes) {} | |||
/* Create client side CSecurity class instance */ | |||
CSecurity* GetCSecurity(rdr::U32 secType); | |||
CSecurity* GetCSecurity(CConnection* cc, rdr::U32 secType); | |||
static void setDefaults(void); | |||
@@ -49,29 +49,29 @@ StringParameter SecurityServer::secTypes | |||
#endif | |||
ConfServer); | |||
SSecurity* SecurityServer::GetSSecurity(U32 secType) | |||
SSecurity* SecurityServer::GetSSecurity(SConnection* sc, U32 secType) | |||
{ | |||
if (!IsSupported(secType)) | |||
goto bail; | |||
switch (secType) { | |||
case secTypeNone: return new SSecurityNone(); | |||
case secTypeVncAuth: return new SSecurityVncAuth(); | |||
case secTypeVeNCrypt: return new SSecurityVeNCrypt(this); | |||
case secTypePlain: return new SSecurityPlain(); | |||
case secTypeNone: return new SSecurityNone(sc); | |||
case secTypeVncAuth: return new SSecurityVncAuth(sc); | |||
case secTypeVeNCrypt: return new SSecurityVeNCrypt(sc, this); | |||
case secTypePlain: return new SSecurityPlain(sc); | |||
#ifdef HAVE_GNUTLS | |||
case secTypeTLSNone: | |||
return new SSecurityStack(secTypeTLSNone, new SSecurityTLS(true)); | |||
return new SSecurityStack(sc, secTypeTLSNone, new SSecurityTLS(sc, true)); | |||
case secTypeTLSVnc: | |||
return new SSecurityStack(secTypeTLSVnc, new SSecurityTLS(true), new SSecurityVncAuth()); | |||
return new SSecurityStack(sc, secTypeTLSVnc, new SSecurityTLS(sc, true), new SSecurityVncAuth(sc)); | |||
case secTypeTLSPlain: | |||
return new SSecurityStack(secTypeTLSPlain, new SSecurityTLS(true), new SSecurityPlain()); | |||
return new SSecurityStack(sc, secTypeTLSPlain, new SSecurityTLS(sc, true), new SSecurityPlain(sc)); | |||
case secTypeX509None: | |||
return new SSecurityStack(secTypeX509None, new SSecurityTLS(false)); | |||
return new SSecurityStack(sc, secTypeX509None, new SSecurityTLS(sc, false)); | |||
case secTypeX509Vnc: | |||
return new SSecurityStack(secTypeX509None, new SSecurityTLS(false), new SSecurityVncAuth()); | |||
return new SSecurityStack(sc, secTypeX509None, new SSecurityTLS(sc, false), new SSecurityVncAuth(sc)); | |||
case secTypeX509Plain: | |||
return new SSecurityStack(secTypeX509Plain, new SSecurityTLS(false), new SSecurityPlain()); | |||
return new SSecurityStack(sc, secTypeX509Plain, new SSecurityTLS(sc, false), new SSecurityPlain(sc)); | |||
#endif | |||
} | |||
@@ -24,7 +24,8 @@ | |||
#include <rfb/Security.h> | |||
namespace rfb { | |||
class SConnection; | |||
class SSecurity; | |||
class SecurityServer : public Security { | |||
@@ -32,7 +33,7 @@ namespace rfb { | |||
SecurityServer(void) : Security(secTypes) {} | |||
/* Create server side SSecurity class instance */ | |||
SSecurity* GetSSecurity(rdr::U32 secType); | |||
SSecurity* GetSSecurity(SConnection* sc, rdr::U32 secType); | |||
static StringParameter secTypes; | |||
}; |
@@ -64,7 +64,8 @@ static const struct TightJPEGConfiguration conf[10] = { | |||
TightJPEGEncoder::TightJPEGEncoder(SConnection* conn) : | |||
Encoder(conn, encodingTight, (EncoderFlags)(EncoderUseNativePF | EncoderLossy), -1), | |||
Encoder(conn, encodingTight, | |||
(EncoderFlags)(EncoderUseNativePF | EncoderLossy), -1, 9), | |||
qualityLevel(-1), fineQuality(-1), fineSubsampling(subsampleUndefined) | |||
{ | |||
} | |||
@@ -101,6 +102,11 @@ void TightJPEGEncoder::setFineQualityLevel(int quality, int subsampling) | |||
fineSubsampling = subsampling; | |||
} | |||
int TightJPEGEncoder::getQualityLevel() | |||
{ | |||
return qualityLevel; | |||
} | |||
void TightJPEGEncoder::writeRect(const PixelBuffer* pb, const Palette& palette) | |||
{ | |||
const rdr::U8* buffer; |
@@ -35,6 +35,8 @@ namespace rfb { | |||
virtual void setQualityLevel(int level); | |||
virtual void setFineQualityLevel(int quality, int subsampling); | |||
virtual int getQualityLevel(); | |||
virtual void writeRect(const PixelBuffer* pb, const Palette& palette); | |||
virtual void writeSolidRect(int width, int height, | |||
const PixelFormat& pf, |
@@ -151,7 +151,7 @@ int Timer::getTimeoutMs() { | |||
int Timer::getRemainingMs() { | |||
timeval now; | |||
gettimeofday(&now, 0); | |||
return __rfbmax(0, diffTimeMillis(pending.front()->dueTime, now)); | |||
return __rfbmax(0, diffTimeMillis(dueTime, now)); | |||
} | |||
bool Timer::isBefore(timeval other) { |
@@ -1,5 +1,6 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* Copyright 2009-2018 Pierre Ossman for Cendio AB | |||
* Copyright 2018 Peter Astrand 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 | |||
@@ -48,7 +49,7 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, | |||
inProcessMessages(false), | |||
pendingSyncFence(false), syncFence(false), fenceFlags(0), | |||
fenceDataLen(0), fenceData(NULL), congestionTimer(this), | |||
server(server_), updates(false), | |||
losslessTimer(this), server(server_), updates(false), | |||
updateRenderedCursor(false), removeRenderedCursor(false), | |||
continuousUpdates(false), encodeManager(this), pointerEventTime(0), | |||
clientHasCursor(false), | |||
@@ -839,7 +840,8 @@ void VNCSConnectionST::supportsLEDState() | |||
bool VNCSConnectionST::handleTimeout(Timer* t) | |||
{ | |||
try { | |||
if (t == &congestionTimer) | |||
if ((t == &congestionTimer) || | |||
(t == &losslessTimer)) | |||
writeFramebufferUpdate(); | |||
} catch (rdr::Exception& e) { | |||
close(e.str()); | |||
@@ -1065,28 +1067,46 @@ void VNCSConnectionST::writeDataUpdate() | |||
} | |||
// Return if there is nothing to send the client. | |||
if (ui.is_empty() && !writer()->needFakeUpdate()) { | |||
int eta; | |||
if (ui.is_empty() && !writer()->needFakeUpdate() && | |||
!encodeManager.needsLosslessRefresh(req)) | |||
return; | |||
// Any lossless refresh that needs handling? | |||
if (!encodeManager.needsLosslessRefresh(req)) | |||
return; | |||
// Now? Or later? | |||
eta = encodeManager.getNextLosslessRefresh(req); | |||
if (eta > 0) { | |||
losslessTimer.start(eta); | |||
return; | |||
} | |||
} | |||
writeRTTPing(); | |||
if (!ui.is_empty()) | |||
encodeManager.writeUpdate(ui, server->getPixelBuffer(), cursor); | |||
else { | |||
size_t maxUpdateSize; | |||
int nextUpdate; | |||
// FIXME: If continuous updates aren't used then the client might | |||
// be slower than frameRate in its requests and we could | |||
// afford a larger update size | |||
nextUpdate = server->msToNextUpdate(); | |||
if (nextUpdate > 0) { | |||
size_t bandwidth, maxUpdateSize; | |||
// FIXME: Bandwidth estimation without congestion control | |||
maxUpdateSize = congestion.getBandwidth() * | |||
server->msToNextUpdate() / 1000; | |||
// FIXME: Bandwidth estimation without congestion control | |||
bandwidth = congestion.getBandwidth(); | |||
encodeManager.writeLosslessRefresh(req, server->getPixelBuffer(), | |||
cursor, maxUpdateSize); | |||
// FIXME: Hard coded value for maximum CPU throughput | |||
if (bandwidth > 5000000) | |||
bandwidth = 5000000; | |||
maxUpdateSize = bandwidth * nextUpdate / 1000; | |||
encodeManager.writeLosslessRefresh(req, server->getPixelBuffer(), | |||
cursor, maxUpdateSize); | |||
} | |||
} | |||
writeRTTPing(); |
@@ -191,6 +191,7 @@ namespace rfb { | |||
Congestion congestion; | |||
Timer congestionTimer; | |||
Timer losslessTimer; | |||
VNCServerST* server; | |||
SimpleUpdateTracker updates; |