Initial commit

This commit is contained in:
matt
2020-09-20 12:16:44 +00:00
parent 09a4460ddb
commit 408c005d3e
839 changed files with 190481 additions and 0 deletions

86
common/rfb/Blacklist.cxx Normal file
View File

@@ -0,0 +1,86 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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/Blacklist.h>
#include <rfb/Configuration.h>
using namespace rfb;
IntParameter Blacklist::threshold("BlacklistThreshold",
"The number of unauthenticated connection attempts allowed from any "
"individual host before that host is black-listed",
5);
IntParameter Blacklist::initialTimeout("BlacklistTimeout",
"The initial timeout applied when a host is first black-listed. "
"The host cannot re-attempt a connection until the timeout expires.",
10);
Blacklist::Blacklist() {
}
Blacklist::~Blacklist() {
// Free the map keys
BlacklistMap::iterator i;
for (i=blm.begin(); i!=blm.end(); i++) {
strFree((char*)(*i).first);
}
}
bool Blacklist::isBlackmarked(const char* name) {
BlacklistMap::iterator i = blm.find(name);
if (i == blm.end()) {
// Entry is not already black-marked.
// Create the entry unmarked, unblocked,
// with suitable defaults set.
BlacklistInfo bi;
bi.marks = 1;
bi.blockUntil = 0;
bi.blockTimeout = initialTimeout;
blm[strDup(name)] = bi;
i = blm.find(name);
}
// Entry exists - has it reached the threshold yet?
if ((*i).second.marks >= threshold) {
// Yes - entry is blocked - has the timeout expired?
time_t now = time(0);
if (now >= (*i).second.blockUntil) {
// Timeout has expired. Reset timeout and allow
// a re-try.
(*i).second.blockUntil = now + (*i).second.blockTimeout;
(*i).second.blockTimeout = (*i).second.blockTimeout * 2;
return false;
}
// Blocked and timeout still in effect - reject!
return true;
}
// We haven't reached the threshold yet.
// Increment the black-mark counter but allow
// the entry to pass.
(*i).second.marks++;
return false;
}
void Blacklist::clearBlackmark(const char* name) {
BlacklistMap::iterator i = blm.find(name);
if (i != blm.end()) {
strFree((char*)(*i).first);
blm.erase(i);
}
}

91
common/rfb/Blacklist.h Normal file
View File

@@ -0,0 +1,91 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
//
// Blacklist.h - Handling of black-listed entities.
// Just keeps a table mapping strings to timing information, including
// how many times the entry has been black-listed and when to next
// put it on probation (e.g. allow a connection in from the host, and
// re-blacklist it if that fails).
//
#ifndef __RFB_BLACKLIST_H__
#define __RFB_BLACKLIST_H__
#include <string.h>
#include <time.h>
#include <map>
#include <rfb/Configuration.h>
#include <rfb/util.h>
namespace rfb {
//
// -=- Blacklist handler
//
// Parameters include a threshold after which to blacklist the named
// host, and a timeout after which to re-consider them.
//
// Threshold means that isBlackmarked can be called that number of times
// before it will return true.
//
// Timeout means that after that many seconds, the next call to isBlackmarked
// will return false. At the same time, the timeout is doubled, so that the
// next calls will fail, until the timeout expires again or clearBlackmark is
// called.
//
// When clearBlackMark is called, the corresponding entry is completely
// removed, causing the next isBlackmarked call to return false.
// KNOWN BUG: Client can keep making rejected requests, thus increasing
// their timeout. If client does this for 30 years, timeout may wrap round
// to a very small value again.
// THIS CLASS IS NOT THREAD-SAFE!
class Blacklist {
public:
Blacklist();
~Blacklist();
bool isBlackmarked(const char* name);
void clearBlackmark(const char* name);
static IntParameter threshold;
static IntParameter initialTimeout;
protected:
struct ltStr {
bool operator()(const char* s1, const char* s2) const {
return strcmp(s1, s2) < 0;
};
};
struct BlacklistInfo {
int marks;
time_t blockUntil;
unsigned int blockTimeout;
};
typedef std::map<const char*,BlacklistInfo,ltStr> BlacklistMap;
BlacklistMap blm;
};
}
#endif

366
common/rfb/CConnection.cxx Normal file
View File

@@ -0,0 +1,366 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2011-2017 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 <string.h>
#include <rfb/Exception.h>
#include <rfb/fenceTypes.h>
#include <rfb/CMsgReader.h>
#include <rfb/CMsgWriter.h>
#include <rfb/CSecurity.h>
#include <rfb/Security.h>
#include <rfb/SecurityClient.h>
#include <rfb/CConnection.h>
#include <rfb/util.h>
#include <rfb/LogWriter.h>
#include <rdr/InStream.h>
#include <rdr/OutStream.h>
using namespace rfb;
static LogWriter vlog("CConnection");
CConnection::CConnection()
: csecurity(0), is(0), os(0), reader_(0), writer_(0),
shared(false),
state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false),
framebuffer(NULL), decoder(this)
{
}
CConnection::~CConnection()
{
setFramebuffer(NULL);
if (csecurity) csecurity->destroy();
delete reader_;
reader_ = 0;
delete writer_;
writer_ = 0;
}
void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
{
is = is_;
os = os_;
}
void CConnection::setFramebuffer(ModifiablePixelBuffer* fb)
{
decoder.flush();
if ((framebuffer != NULL) && (fb != NULL)) {
Rect rect;
const rdr::U8* data;
int stride;
const rdr::U8 black[4] = { 0, 0, 0, 0 };
// Copy still valid area
rect.setXYWH(0, 0,
__rfbmin(fb->width(), framebuffer->width()),
__rfbmin(fb->height(), framebuffer->height()));
data = framebuffer->getBuffer(framebuffer->getRect(), &stride);
fb->imageRect(rect, data, stride);
// Black out any new areas
if (fb->width() > framebuffer->width()) {
rect.setXYWH(framebuffer->width(), 0,
fb->width() - framebuffer->width(),
fb->height());
fb->fillRect(rect, black);
}
if (fb->height() > framebuffer->height()) {
rect.setXYWH(0, framebuffer->height(),
fb->width(),
fb->height() - framebuffer->height());
fb->fillRect(rect, black);
}
}
delete framebuffer;
framebuffer = fb;
}
void CConnection::initialiseProtocol()
{
state_ = RFBSTATE_PROTOCOL_VERSION;
}
void CConnection::processMsg()
{
switch (state_) {
case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break;
case RFBSTATE_SECURITY: processSecurityMsg(); break;
case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break;
case RFBSTATE_INITIALISATION: processInitMsg(); break;
case RFBSTATE_NORMAL: reader_->readMsg(); break;
case RFBSTATE_UNINITIALISED:
throw Exception("CConnection::processMsg: not initialised yet?");
default:
throw Exception("CConnection::processMsg: invalid state");
}
}
void CConnection::processVersionMsg()
{
vlog.debug("reading protocol version");
bool done;
if (!cp.readVersion(is, &done)) {
state_ = RFBSTATE_INVALID;
throw Exception("reading version failed: not an RFB server?");
}
if (!done) return;
vlog.info("Server supports RFB protocol version %d.%d",
cp.majorVersion, cp.minorVersion);
// The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
if (cp.beforeVersion(3,3)) {
vlog.error("Server gave unsupported RFB protocol version %d.%d",
cp.majorVersion, cp.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);
}
cp.writeVersion(os);
state_ = RFBSTATE_SECURITY_TYPES;
vlog.info("Using RFB protocol version %d.%d",
cp.majorVersion, cp.minorVersion);
}
void CConnection::processSecurityTypesMsg()
{
vlog.debug("processing security types message");
int secType = secTypeInvalid;
std::list<rdr::U8> secTypes;
secTypes = security.GetEnabledSecTypes();
if (cp.isVersion(3,3)) {
// legacy 3.3 server may only offer "vnc authentication" or "none"
secType = is->readU32();
if (secType == secTypeInvalid) {
throwConnFailedException();
} else if (secType == secTypeNone || secType == secTypeVncAuth) {
std::list<rdr::U8>::iterator i;
for (i = secTypes.begin(); i != secTypes.end(); i++)
if (*i == secType) {
secType = *i;
break;
}
if (i == secTypes.end())
secType = secTypeInvalid;
} else {
vlog.error("Unknown 3.3 security type %d", secType);
throw Exception("Unknown 3.3 security type");
}
} else {
// >=3.7 server will offer us a list
int nServerSecTypes = is->readU8();
if (nServerSecTypes == 0)
throwConnFailedException();
std::list<rdr::U8>::iterator j;
for (int i = 0; i < nServerSecTypes; i++) {
rdr::U8 serverSecType = is->readU8();
vlog.debug("Server offers security type %s(%d)",
secTypeName(serverSecType), serverSecType);
/*
* Use the first type sent by server which matches client's type.
* It means server's order specifies priority.
*/
if (secType == secTypeInvalid) {
for (j = secTypes.begin(); j != secTypes.end(); j++)
if (*j == serverSecType) {
secType = *j;
break;
}
}
}
// Inform the server of our decision
if (secType != secTypeInvalid) {
os->writeU8(secType);
os->flush();
vlog.info("Choosing security type %s(%d)",secTypeName(secType),secType);
}
}
if (secType == secTypeInvalid) {
state_ = RFBSTATE_INVALID;
vlog.error("No matching security types");
throw Exception("No matching security types");
}
state_ = RFBSTATE_SECURITY;
csecurity = security.GetCSecurity(secType);
processSecurityMsg();
}
void CConnection::processSecurityMsg()
{
vlog.debug("processing security message");
if (csecurity->processMsg(this)) {
state_ = RFBSTATE_SECURITY_RESULT;
processSecurityResultMsg();
}
}
void CConnection::processSecurityResultMsg()
{
vlog.debug("processing security result message");
int result;
if (cp.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
result = secResultOK;
} else {
if (!is->checkNoWait(1)) return;
result = is->readU32();
}
switch (result) {
case secResultOK:
securityCompleted();
return;
case secResultFailed:
vlog.debug("auth failed");
break;
case secResultTooMany:
vlog.debug("auth failed - too many tries");
break;
default:
throw Exception("Unknown security result from server");
}
state_ = RFBSTATE_INVALID;
if (cp.beforeVersion(3,8))
throw AuthFailureException();
CharArray reason(is->readString());
throw AuthFailureException(reason.buf);
}
void CConnection::processInitMsg()
{
vlog.debug("reading server initialisation");
reader_->readServerInit();
}
void CConnection::throwConnFailedException()
{
state_ = RFBSTATE_INVALID;
CharArray reason;
reason.buf = is->readString();
throw ConnFailedException(reason.buf);
}
void CConnection::securityCompleted()
{
state_ = RFBSTATE_INITIALISATION;
reader_ = new CMsgReader(this, is);
writer_ = new CMsgWriter(&cp, os);
vlog.debug("Authentication success!");
authSuccess();
writer_->writeClientInit(shared);
}
void CConnection::setDesktopSize(int w, int h)
{
decoder.flush();
CMsgHandler::setDesktopSize(w,h);
}
void CConnection::setExtendedDesktopSize(unsigned reason,
unsigned result,
int w, int h,
const ScreenSet& layout)
{
decoder.flush();
CMsgHandler::setExtendedDesktopSize(reason, result, w, h, layout);
}
void CConnection::readAndDecodeRect(const Rect& r, int encoding,
ModifiablePixelBuffer* pb)
{
decoder.decodeRect(r, encoding, pb);
decoder.flush();
}
void CConnection::framebufferUpdateStart()
{
CMsgHandler::framebufferUpdateStart();
}
void CConnection::framebufferUpdateEnd()
{
decoder.flush();
CMsgHandler::framebufferUpdateEnd();
}
void CConnection::dataRect(const Rect& r, int encoding)
{
decoder.decodeRect(r, encoding, framebuffer);
}
void CConnection::authSuccess()
{
}
void CConnection::serverInit()
{
state_ = RFBSTATE_NORMAL;
vlog.debug("initialisation done");
}
void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
{
CMsgHandler::fence(flags, len, data);
if (!(flags & fenceFlagRequest))
return;
// We cannot guarantee any synchronisation at this level
flags = 0;
writer()->writeFence(flags, len, data);
}

195
common/rfb/CConnection.h Normal file
View File

@@ -0,0 +1,195 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2011-2017 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.
*/
//
// CConnection - class on the client side representing a connection to a
// server. A derived class should override methods appropriately.
//
#ifndef __RFB_CCONNECTION_H__
#define __RFB_CCONNECTION_H__
#include <rfb/CMsgHandler.h>
#include <rfb/DecodeManager.h>
#include <rfb/SecurityClient.h>
#include <rfb/util.h>
namespace rfb {
class CMsgReader;
class CMsgWriter;
class CSecurity;
class IdentityVerifier;
class CConnection : public CMsgHandler {
public:
CConnection();
virtual ~CConnection();
// Methods to initialise the connection
// setServerName() is used to provide a unique(ish) name for the server to
// which we are connected. This might be the result of getPeerEndpoint on
// a TcpSocket, for example, or a host specified by DNS name & port.
// The serverName is used when verifying the Identity of a host (see RA2).
void setServerName(const char* name_) { serverName.replaceBuf(strDup(name_)); }
// setStreams() sets the streams to be used for the connection. These must
// be set before initialiseProtocol() and processMsg() are called. The
// CSecurity object may call setStreams() again to provide alternative
// streams over which the RFB protocol is sent (i.e. encrypting/decrypting
// streams). Ownership of the streams remains with the caller
// (i.e. SConnection will not delete them).
void setStreams(rdr::InStream* is, rdr::OutStream* os);
// setShared sets the value of the shared flag which will be sent to the
// server upon initialisation.
void setShared(bool s) { shared = s; }
// setProtocol3_3 configures whether or not the CConnection should
// only ever support protocol version 3.3
void setProtocol3_3(bool s) {useProtocol3_3 = s;}
// setFramebuffer configures the PixelBuffer that the CConnection
// should render all pixel data in to. Note that the CConnection
// takes ownership of the PixelBuffer and it must not be deleted by
// anyone else. Call setFramebuffer again with NULL or a different
// PixelBuffer to delete the previous one.
void setFramebuffer(ModifiablePixelBuffer* fb);
// initialiseProtocol() should be called once the streams and security
// types are set. Subsequently, processMsg() should be called whenever
// there is data to read on the InStream.
void initialiseProtocol();
// processMsg() should be called whenever there is either:
// - data available on the underlying network stream
// In this case, processMsg may return without processing an RFB message,
// if the available data does not result in an RFB message being ready
// to handle. e.g. if data is encrypted.
// NB: This makes it safe to call processMsg() in response to select()
// - data available on the CConnection's current InStream
// In this case, processMsg should always process the available RFB
// message before returning.
// NB: In either case, you must have called initialiseProtocol() first.
void processMsg();
// Methods overridden from CMsgHandler
// Note: These must be called by any deriving classes
virtual void setDesktopSize(int w, int h);
virtual void setExtendedDesktopSize(unsigned reason, unsigned result,
int w, int h,
const ScreenSet& layout);
virtual void readAndDecodeRect(const Rect& r, int encoding,
ModifiablePixelBuffer* pb);
virtual void framebufferUpdateStart();
virtual void framebufferUpdateEnd();
virtual void dataRect(const Rect& r, int encoding);
// 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();
// Other methods
CMsgReader* reader() { return reader_; }
CMsgWriter* writer() { return writer_; }
rdr::InStream* getInStream() { return is; }
rdr::OutStream* getOutStream() { return os; }
// Access method used by SSecurity implementations that can verify servers'
// Identities, to determine the unique(ish) name of the server.
const char* getServerName() const { return serverName.buf; }
bool isSecure() const { return csecurity ? csecurity->isSecure() : false; }
enum stateEnum {
RFBSTATE_UNINITIALISED,
RFBSTATE_PROTOCOL_VERSION,
RFBSTATE_SECURITY_TYPES,
RFBSTATE_SECURITY,
RFBSTATE_SECURITY_RESULT,
RFBSTATE_INITIALISATION,
RFBSTATE_NORMAL,
RFBSTATE_INVALID
};
stateEnum state() { return state_; }
CSecurity *csecurity;
SecurityClient security;
protected:
void setState(stateEnum s) { state_ = s; }
void setReader(CMsgReader *r) { reader_ = r; }
void setWriter(CMsgWriter *w) { writer_ = w; }
ModifiablePixelBuffer* getFramebuffer() { return framebuffer; }
private:
// This is a default implementation of fences that automatically
// responds to requests, stating no support for synchronisation.
// When overriding, call CMsgHandler::fence() directly in order to
// state correct support for fence flags.
virtual void fence(rdr::U32 flags, unsigned len, const char data[]);
private:
void processVersionMsg();
void processSecurityTypesMsg();
void processSecurityMsg();
void processSecurityResultMsg();
void processInitMsg();
void throwAuthFailureException();
void throwConnFailedException();
void securityCompleted();
rdr::InStream* is;
rdr::OutStream* os;
CMsgReader* reader_;
CMsgWriter* writer_;
bool deleteStreamsWhenDone;
bool shared;
stateEnum state_;
CharArray serverName;
bool useProtocol3_3;
ModifiablePixelBuffer* framebuffer;
DecodeManager decoder;
};
}
#endif

105
common/rfb/CMakeLists.txt Normal file
View File

@@ -0,0 +1,105 @@
include_directories(${CMAKE_SOURCE_DIR}/common ${JPEG_INCLUDE_DIR})
set(RFB_SOURCES
Blacklist.cxx
Congestion.cxx
CConnection.cxx
CMsgHandler.cxx
CMsgReader.cxx
CMsgWriter.cxx
CSecurityPlain.cxx
CSecurityStack.cxx
CSecurityVeNCrypt.cxx
CSecurityVncAuth.cxx
ComparingUpdateTracker.cxx
Configuration.cxx
ConnParams.cxx
CopyRectDecoder.cxx
Cursor.cxx
DecodeManager.cxx
Decoder.cxx
d3des.c
EncCache.cxx
EncodeManager.cxx
Encoder.cxx
HTTPServer.cxx
HextileDecoder.cxx
HextileEncoder.cxx
JpegCompressor.cxx
JpegDecompressor.cxx
KeyRemapper.cxx
LogWriter.cxx
Logger.cxx
Logger_file.cxx
Logger_stdio.cxx
Password.cxx
PixelBuffer.cxx
PixelFormat.cxx
RREEncoder.cxx
RREDecoder.cxx
RawDecoder.cxx
RawEncoder.cxx
Region.cxx
SConnection.cxx
SMsgHandler.cxx
SMsgReader.cxx
SMsgWriter.cxx
ServerCore.cxx
Security.cxx
SecurityServer.cxx
SecurityClient.cxx
SSecurityPlain.cxx
SSecurityStack.cxx
SSecurityVncAuth.cxx
SSecurityVeNCrypt.cxx
ScaleFilters.cxx
Timer.cxx
TightDecoder.cxx
TightEncoder.cxx
TightJPEGEncoder.cxx
TightWEBPEncoder.cxx
UpdateTracker.cxx
VNCSConnectionST.cxx
VNCServerST.cxx
ZRLEEncoder.cxx
ZRLEDecoder.cxx
encodings.cxx
util.cxx
xxhash.c)
if(UNIX)
set(RFB_SOURCES ${RFB_SOURCES} Logger_syslog.cxx)
endif()
if(WIN32)
include_directories(${CMAKE_SOURCE_DIR}/win)
set(RFB_SOURCES ${RFB_SOURCES} WinPasswdValidator.cxx)
endif(WIN32)
set(RFB_LIBRARIES ${JPEG_LIBRARIES} os rdr Xregion)
if(HAVE_PAM)
set(RFB_SOURCES ${RFB_SOURCES} UnixPasswordValidator.cxx
UnixPasswordValidator.h pam.c pam.h)
set(RFB_LIBRARIES ${RFB_LIBRARIES} ${PAM_LIBS})
endif()
if(GNUTLS_FOUND)
set(RFB_SOURCES
${RFB_SOURCES}
CSecurityTLS.cxx
SSecurityTLS.cxx
)
set(RFB_LIBRARIES
${RFB_LIBRARIES}
${GNUTLS_LIBRARIES}
)
endif()
add_library(rfb STATIC ${RFB_SOURCES})
target_link_libraries(rfb ${RFB_LIBRARIES})
if(UNIX)
libtool_create_control_file(rfb)
endif()

View File

@@ -0,0 +1,94 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-2011 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 <rfb/Exception.h>
#include <rfb/CMsgHandler.h>
#include <rfb/screenTypes.h>
using namespace rfb;
CMsgHandler::CMsgHandler()
{
}
CMsgHandler::~CMsgHandler()
{
}
void CMsgHandler::setDesktopSize(int width, int height)
{
cp.width = width;
cp.height = height;
}
void CMsgHandler::setExtendedDesktopSize(unsigned reason, unsigned result,
int width, int height,
const ScreenSet& layout)
{
cp.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;
}
void CMsgHandler::setPixelFormat(const PixelFormat& pf)
{
cp.setPF(pf);
}
void CMsgHandler::setName(const char* name)
{
cp.setName(name);
}
void CMsgHandler::fence(rdr::U32 flags, unsigned len, const char data[])
{
cp.supportsFence = true;
}
void CMsgHandler::endOfContinuousUpdates()
{
cp.supportsContinuousUpdates = true;
}
void CMsgHandler::supportsQEMUKeyEvent()
{
cp.supportsQEMUKeyEvent = true;
}
void CMsgHandler::framebufferUpdateStart()
{
}
void CMsgHandler::framebufferUpdateEnd()
{
}
void CMsgHandler::setLEDState(unsigned int state)
{
cp.setLEDState(state);
}

78
common/rfb/CMsgHandler.h Normal file
View File

@@ -0,0 +1,78 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-2011 Pierre Ossman for Cendio AB
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
*
* 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.
*/
//
// CMsgHandler - class to handle incoming messages on the client side.
//
#ifndef __RFB_CMSGHANDLER_H__
#define __RFB_CMSGHANDLER_H__
#include <rdr/types.h>
#include <rfb/Pixel.h>
#include <rfb/ConnParams.h>
#include <rfb/Rect.h>
#include <rfb/ScreenSet.h>
namespace rdr { class InStream; }
namespace rfb {
class CMsgHandler {
public:
CMsgHandler();
virtual ~CMsgHandler();
// 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.
virtual void setDesktopSize(int w, int h);
virtual void setExtendedDesktopSize(unsigned reason, unsigned result,
int w, int h,
const ScreenSet& layout);
virtual void setCursor(int width, int height, const Point& hotspot,
const rdr::U8* data) = 0;
virtual void setPixelFormat(const PixelFormat& pf);
virtual void setName(const char* name);
virtual void fence(rdr::U32 flags, unsigned len, const char data[]);
virtual void endOfContinuousUpdates();
virtual void supportsQEMUKeyEvent();
virtual void serverInit() = 0;
virtual void readAndDecodeRect(const Rect& r, int encoding,
ModifiablePixelBuffer* pb) = 0;
virtual void framebufferUpdateStart();
virtual void framebufferUpdateEnd();
virtual void dataRect(const Rect& r, int encoding) = 0;
virtual void setColourMapEntries(int firstColour, int nColours,
rdr::U16* rgbs) = 0;
virtual void bell() = 0;
virtual void serverCutText(const char* str, rdr::U32 len) = 0;
virtual void setLEDState(unsigned int state);
ConnParams cp;
};
}
#endif

398
common/rfb/CMsgReader.cxx Normal file
View File

@@ -0,0 +1,398 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-2017 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 <assert.h>
#include <stdio.h>
#include <rfb/msgTypes.h>
#include <rdr/InStream.h>
#include <rfb/Exception.h>
#include <rfb/util.h>
#include <rfb/CMsgHandler.h>
#include <rfb/CMsgReader.h>
using namespace rfb;
CMsgReader::CMsgReader(CMsgHandler* handler_, rdr::InStream* is_)
: imageBufIdealSize(0), handler(handler_), is(is_),
nUpdateRectsLeft(0)
{
}
CMsgReader::~CMsgReader()
{
}
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();
}
void CMsgReader::readMsg()
{
if (nUpdateRectsLeft == 0) {
int type = is->readU8();
switch (type) {
case msgTypeSetColourMapEntries:
readSetColourMapEntries();
break;
case msgTypeBell:
readBell();
break;
case msgTypeServerCutText:
readServerCutText();
break;
case msgTypeFramebufferUpdate:
readFramebufferUpdate();
break;
case msgTypeServerFence:
readFence();
break;
case msgTypeEndOfContinuousUpdates:
readEndOfContinuousUpdates();
break;
default:
fprintf(stderr, "unknown message type %d\n", type);
throw Exception("unknown message type");
}
} else {
int x = is->readU16();
int y = is->readU16();
int w = is->readU16();
int h = is->readU16();
int encoding = is->readS32();
switch (encoding) {
case pseudoEncodingLastRect:
nUpdateRectsLeft = 1; // this rectangle is the last one
break;
case pseudoEncodingXCursor:
readSetXCursor(w, h, Point(x,y));
break;
case pseudoEncodingCursor:
readSetCursor(w, h, Point(x,y));
break;
case pseudoEncodingCursorWithAlpha:
readSetCursorWithAlpha(w, h, Point(x,y));
break;
case pseudoEncodingDesktopName:
readSetDesktopName(x, y, w, h);
break;
case pseudoEncodingDesktopSize:
handler->setDesktopSize(w, h);
break;
case pseudoEncodingExtendedDesktopSize:
readExtendedDesktopSize(x, y, w, h);
break;
case pseudoEncodingLEDState:
readLEDState();
case pseudoEncodingQEMUKeyEvent:
handler->supportsQEMUKeyEvent();
break;
default:
readRect(Rect(x, y, x+w, y+h), encoding);
break;
};
nUpdateRectsLeft--;
if (nUpdateRectsLeft == 0)
handler->framebufferUpdateEnd();
}
}
void CMsgReader::readSetColourMapEntries()
{
is->skip(1);
int firstColour = is->readU16();
int nColours = is->readU16();
rdr::U16Array rgbs(nColours * 3);
for (int i = 0; i < nColours * 3; i++)
rgbs.buf[i] = is->readU16();
handler->setColourMapEntries(firstColour, nColours, rgbs.buf);
}
void CMsgReader::readBell()
{
handler->bell();
}
void CMsgReader::readServerCutText()
{
is->skip(3);
rdr::U32 len = is->readU32();
if (len > 256*1024) {
is->skip(len);
fprintf(stderr,"cut text too long (%d bytes) - ignoring\n",len);
return;
}
CharArray ca(len+1);
ca.buf[len] = 0;
is->readBytes(ca.buf, len);
handler->serverCutText(ca.buf, len);
}
void CMsgReader::readFence()
{
rdr::U32 flags;
rdr::U8 len;
char data[64];
is->skip(3);
flags = is->readU32();
len = is->readU8();
if (len > sizeof(data)) {
fprintf(stderr, "Ignoring fence with too large payload\n");
is->skip(len);
return;
}
is->readBytes(data, len);
handler->fence(flags, len, data);
}
void CMsgReader::readEndOfContinuousUpdates()
{
handler->endOfContinuousUpdates();
}
void CMsgReader::readFramebufferUpdate()
{
is->skip(1);
nUpdateRectsLeft = is->readU16();
handler->framebufferUpdateStart();
}
void CMsgReader::readRect(const Rect& r, int encoding)
{
if ((r.br.x > handler->cp.width) || (r.br.y > handler->cp.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);
throw Exception("Rect too big");
}
if (r.is_empty())
fprintf(stderr, "Warning: zero size rect\n");
handler->dataRect(r, encoding);
}
void CMsgReader::readSetXCursor(int width, int height, const Point& hotspot)
{
if (width > maxCursorSize || height > maxCursorSize)
throw Exception("Too big cursor");
rdr::U8 buf[width*height*4];
if (width * height > 0) {
rdr::U8 pr, pg, pb;
rdr::U8 sr, sg, sb;
int data_len = ((width+7)/8) * height;
int mask_len = ((width+7)/8) * height;
rdr::U8Array data(data_len);
rdr::U8Array mask(mask_len);
int x, y;
rdr::U8* out;
pr = is->readU8();
pg = is->readU8();
pb = is->readU8();
sr = is->readU8();
sg = is->readU8();
sb = is->readU8();
is->readBytes(data.buf, data_len);
is->readBytes(mask.buf, mask_len);
int maskBytesPerRow = (width+7)/8;
out = buf;
for (y = 0;y < height;y++) {
for (x = 0;x < width;x++) {
int byte = y * maskBytesPerRow + x / 8;
int bit = 7 - x % 8;
if (data.buf[byte] & (1 << bit)) {
out[0] = pr;
out[1] = pg;
out[2] = pb;
} else {
out[0] = sr;
out[1] = sg;
out[2] = sb;
}
if (mask.buf[byte] & (1 << bit))
out[3] = 255;
else
out[3] = 0;
out += 4;
}
}
}
handler->setCursor(width, height, hotspot, buf);
}
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 mask_len = ((width+7)/8) * height;
rdr::U8Array data(data_len);
rdr::U8Array mask(mask_len);
int x, y;
rdr::U8 buf[width*height*4];
rdr::U8* in;
rdr::U8* out;
is->readBytes(data.buf, data_len);
is->readBytes(mask.buf, mask_len);
int maskBytesPerRow = (width+7)/8;
in = data.buf;
out = buf;
for (y = 0;y < height;y++) {
for (x = 0;x < width;x++) {
int byte = y * maskBytesPerRow + x / 8;
int bit = 7 - x % 8;
handler->cp.pf().rgbFromBuffer(out, in, 1);
if (mask.buf[byte] & (1 << bit))
out[3] = 255;
else
out[3] = 0;
in += handler->cp.pf().bpp/8;
out += 4;
}
}
handler->setCursor(width, height, hotspot, buf);
}
void CMsgReader::readSetCursorWithAlpha(int width, int height, const Point& hotspot)
{
if (width > maxCursorSize || height > maxCursorSize)
throw Exception("Too big cursor");
int encoding;
const PixelFormat rgbaPF(32, 32, false, true, 255, 255, 255, 16, 8, 0);
ManagedPixelBuffer pb(rgbaPF, width, height);
PixelFormat origPF;
rdr::U8* buf;
int stride;
encoding = is->readS32();
origPF = handler->cp.pf();
handler->cp.setPF(rgbaPF);
handler->readAndDecodeRect(pb.getRect(), encoding, &pb);
handler->cp.setPF(origPF);
// On-wire data has pre-multiplied alpha, but we store it
// non-pre-multiplied
buf = pb.getBufferRW(pb.getRect(), &stride);
assert(stride == width);
for (int i = 0;i < pb.area();i++) {
rdr::U8 alpha;
alpha = buf[3];
if (alpha == 0)
alpha = 1; // Avoid division by zero
buf[0] = (unsigned)buf[0] * 255/alpha;
buf[1] = (unsigned)buf[1] * 255/alpha;
buf[2] = (unsigned)buf[2] * 255/alpha;
buf += 4;
}
pb.commitBufferRW(pb.getRect());
handler->setCursor(width, height, hotspot,
pb.getBuffer(pb.getRect(), &stride));
}
void CMsgReader::readSetDesktopName(int x, int y, int w, int h)
{
char* name = is->readString();
if (x || y || w || h) {
fprintf(stderr, "Ignoring DesktopName rect with non-zero position/size\n");
} else {
handler->setName(name);
}
delete [] name;
}
void CMsgReader::readExtendedDesktopSize(int x, int y, int w, int h)
{
unsigned int screens, i;
rdr::U32 id, flags;
int sx, sy, sw, sh;
ScreenSet layout;
screens = is->readU8();
is->skip(3);
for (i = 0;i < screens;i++) {
id = is->readU32();
sx = is->readU16();
sy = is->readU16();
sw = is->readU16();
sh = is->readU16();
flags = is->readU32();
layout.add_screen(Screen(id, sx, sy, sw, sh, flags));
}
handler->setExtendedDesktopSize(x, y, w, h, layout);
}
void CMsgReader::readLEDState()
{
rdr::U8 state;
state = is->readU8();
handler->setLEDState(state);
}

77
common/rfb/CMsgReader.h Normal file
View File

@@ -0,0 +1,77 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-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.
*/
//
// CMsgReader - class for reading RFB messages on the server side
// (i.e. messages from client to server).
//
#ifndef __RFB_CMSGREADER_H__
#define __RFB_CMSGREADER_H__
#include <rdr/types.h>
#include <rfb/Rect.h>
#include <rfb/encodings.h>
namespace rdr { class InStream; }
namespace rfb {
class CMsgHandler;
struct Rect;
class CMsgReader {
public:
CMsgReader(CMsgHandler* handler, rdr::InStream* is);
virtual ~CMsgReader();
void readServerInit();
// readMsg() reads a message, calling the handler as appropriate.
void readMsg();
rdr::InStream* getInStream() { return is; }
int imageBufIdealSize;
protected:
void readSetColourMapEntries();
void readBell();
void readServerCutText();
void readFence();
void readEndOfContinuousUpdates();
void readFramebufferUpdate();
void readRect(const Rect& r, int encoding);
void readSetXCursor(int width, int height, const Point& hotspot);
void readSetCursor(int width, int height, const Point& hotspot);
void readSetCursorWithAlpha(int width, int height, const Point& hotspot);
void readSetDesktopName(int x, int y, int w, int h);
void readExtendedDesktopSize(int x, int y, int w, int h);
void readLEDState();
CMsgHandler* handler;
rdr::InStream* is;
int nUpdateRectsLeft;
static const int maxCursorSize = 256;
};
}
#endif

276
common/rfb/CMsgWriter.cxx Normal file
View File

@@ -0,0 +1,276 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-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/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/CMsgWriter.h>
using namespace rfb;
CMsgWriter::CMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
: cp(cp_), os(os_)
{
}
CMsgWriter::~CMsgWriter()
{
}
void CMsgWriter::writeClientInit(bool shared)
{
os->writeU8(shared);
endMsg();
}
void CMsgWriter::writeSetPixelFormat(const PixelFormat& pf)
{
startMsg(msgTypeSetPixelFormat);
os->pad(3);
pf.write(os);
endMsg();
}
void CMsgWriter::writeSetEncodings(int nEncodings, rdr::U32* encodings)
{
startMsg(msgTypeSetEncodings);
os->skip(1);
os->writeU16(nEncodings);
for (int i = 0; i < nEncodings; i++)
os->writeU32(encodings[i]);
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)
throw Exception("Server does not support SetDesktopSize");
startMsg(msgTypeSetDesktopSize);
os->pad(1);
os->writeU16(width);
os->writeU16(height);
os->writeU8(layout.num_screens());
os->pad(1);
ScreenSet::const_iterator iter;
for (iter = layout.begin();iter != layout.end();++iter) {
os->writeU32(iter->id);
os->writeU16(iter->dimensions.tl.x);
os->writeU16(iter->dimensions.tl.y);
os->writeU16(iter->dimensions.width());
os->writeU16(iter->dimensions.height());
os->writeU32(iter->flags);
}
endMsg();
}
void CMsgWriter::writeFramebufferUpdateRequest(const Rect& r, bool incremental)
{
startMsg(msgTypeFramebufferUpdateRequest);
os->writeU8(incremental);
os->writeU16(r.tl.x);
os->writeU16(r.tl.y);
os->writeU16(r.width());
os->writeU16(r.height());
endMsg();
}
void CMsgWriter::writeEnableContinuousUpdates(bool enable,
int x, int y, int w, int h)
{
if (!cp->supportsContinuousUpdates)
throw Exception("Server does not support continuous updates");
startMsg(msgTypeEnableContinuousUpdates);
os->writeU8(!!enable);
os->writeU16(x);
os->writeU16(y);
os->writeU16(w);
os->writeU16(h);
endMsg();
}
void CMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
{
if (!cp->supportsFence)
throw Exception("Server does not support fences");
if (len > 64)
throw Exception("Too large fence payload");
if ((flags & ~fenceFlagsSupported) != 0)
throw Exception("Unknown fence flags");
startMsg(msgTypeClientFence);
os->pad(3);
os->writeU32(flags);
os->writeU8(len);
os->writeBytes(data, len);
endMsg();
}
void CMsgWriter::writeKeyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
{
if (!cp->supportsQEMUKeyEvent || !keycode) {
/* This event isn't meaningful without a valid keysym */
if (!keysym)
return;
startMsg(msgTypeKeyEvent);
os->writeU8(down);
os->pad(2);
os->writeU32(keysym);
endMsg();
} else {
startMsg(msgTypeQEMUClientMessage);
os->writeU8(qemuExtendedKeyEvent);
os->writeU16(down);
os->writeU32(keysym);
os->writeU32(keycode);
endMsg();
}
}
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;
startMsg(msgTypePointerEvent);
os->writeU8(buttonMask);
os->writeU16(p.x);
os->writeU16(p.y);
endMsg();
}
void CMsgWriter::writeClientCutText(const char* str, rdr::U32 len)
{
startMsg(msgTypeClientCutText);
os->pad(3);
os->writeU32(len);
os->writeBytes(str, len);
endMsg();
}
void CMsgWriter::startMsg(int type)
{
os->writeU8(type);
}
void CMsgWriter::endMsg()
{
os->flush();
}

67
common/rfb/CMsgWriter.h Normal file
View File

@@ -0,0 +1,67 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-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.
*/
//
// CMsgWriter - class for writing RFB messages on the server side.
//
#ifndef __RFB_CMSGWRITER_H__
#define __RFB_CMSGWRITER_H__
#include <rdr/types.h>
namespace rdr { class OutStream; }
namespace rfb {
class PixelFormat;
class ConnParams;
struct ScreenSet;
struct Point;
struct Rect;
class CMsgWriter {
public:
CMsgWriter(ConnParams* cp, 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 writeSetDesktopSize(int width, int height, const ScreenSet& layout);
void writeFramebufferUpdateRequest(const Rect& r,bool incremental);
void writeEnableContinuousUpdates(bool enable, int x, int y, int w, int h);
void writeFence(rdr::U32 flags, unsigned len, const char data[]);
void writeKeyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
void writePointerEvent(const Point& pos, int buttonMask);
void writeClientCutText(const char* str, rdr::U32 len);
protected:
void startMsg(int type);
void endMsg();
ConnParams* cp;
rdr::OutStream* os;
};
}
#endif

61
common/rfb/CSecurity.h Normal file
View File

@@ -0,0 +1,61 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
//
// CSecurity - class on the client side for handling security handshaking. A
// derived class for a particular security type overrides the processMsg()
// method.
// processMsg() is called first when the security type has been decided on, and
// will keep being called whenever there is data to read from the server. It
// should return false when it needs more data, or true when the security
// handshaking is over and we are now waiting for the SecurityResult message
// from the server. In the event of failure a suitable exception should be
// thrown.
//
// Note that the first time processMsg() is called, there is no guarantee that
// there is any data to read from the CConnection's InStream, but subsequent
// calls guarantee there is at least one byte which can be read without
// blocking.
//
// description is a string describing the level of encryption applied to the
// session, or null if no encryption will be used.
#ifndef __RFB_CSECURITY_H__
#define __RFB_CSECURITY_H__
#include <rfb/UserPasswdGetter.h>
namespace rfb {
class CConnection;
class CSecurity {
public:
virtual ~CSecurity() {}
virtual bool processMsg(CConnection* cc)=0;
virtual void destroy() { delete this; }
virtual int getType() const = 0;
virtual const char* description() const = 0;
virtual bool isSecure() const { return false; }
/*
* Use variable directly instead of dumb get/set methods.
* It MUST be set by viewer.
*/
static UserPasswdGetter *upg;
};
}
#endif

View File

@@ -0,0 +1,37 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
//
// CSecurityNone.h
//
#ifndef __CSECURITYNONE_H__
#define __CSECURITYNONE_H__
#include <rfb/Security.h>
#include <rfb/CSecurity.h>
namespace rfb {
class CSecurityNone : public CSecurity {
public:
virtual bool processMsg(CConnection* cc) { return true; }
virtual int getType() const {return secTypeNone;}
virtual const char* description() const {return "No Encryption";}
};
}
#endif

View File

@@ -0,0 +1,45 @@
/* Copyright (C) 2005 Martin Koegler
* Copyright (C) 2010 TigerVNC Team
*
* 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/CConnection.h>
#include <rfb/CSecurityPlain.h>
#include <rfb/UserPasswdGetter.h>
#include <rfb/util.h>
#include <rdr/OutStream.h>
using namespace rfb;
bool CSecurityPlain::processMsg(CConnection* cc)
{
rdr::OutStream* os = cc->getOutStream();
CharArray username;
CharArray password;
(CSecurity::upg)->getUserPasswd(cc->isSecure(), &username.buf, &password.buf);
// Return the response to the server
os->writeU32(strlen(username.buf));
os->writeU32(strlen(password.buf));
os->writeBytes(username.buf,strlen(username.buf));
os->writeBytes(password.buf,strlen(password.buf));
os->flush();
return true;
}

View File

@@ -0,0 +1,35 @@
/* Copyright (C) 2005 Martin Koegler
* Copyright (C) 2010 TigerVNC Team
*
* 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.
*/
#ifndef __RFB_CSECURITYPLAIN_H__
#define __RFB_CSECURITYPLAIN_H__
#include <rfb/CSecurity.h>
#include <rfb/Security.h>
namespace rfb {
class CSecurityPlain : public CSecurity {
public:
CSecurityPlain() {}
virtual bool processMsg(CConnection* cc);
virtual int getType() const { return secTypePlain; }
virtual const char* description() const { return "ask for username and password"; }
};
}
#endif

View File

@@ -0,0 +1,74 @@
/* Copyright (C) 2005 Martin Koegler
* Copyright (C) 2010 TigerVNC Team
*
* 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/CSecurityStack.h>
using namespace rfb;
CSecurityStack::CSecurityStack(int Type, const char*Name, CSecurity* s0,
CSecurity* s1)
:name(Name),type(Type)
{
state = 0;
state0 = s0;
state1 = s1;
}
CSecurityStack::~CSecurityStack()
{
if (state0)
delete state0;
if (state1)
delete state1;
}
bool CSecurityStack::processMsg(CConnection* cc)
{
bool res=true;
if (state == 0) {
if (state0)
res = state0->processMsg(cc);
if (!res)
return res;
state++;
}
if (state == 1) {
if(state1)
res = state1->processMsg(cc);
if(!res)
return res;
state++;
}
return res;
}
bool CSecurityStack::isSecure() const
{
if (state0 && state0->isSecure())
return true;
if (state == 1 && state1 && state1->isSecure())
return true;
return false;
}

View File

@@ -0,0 +1,44 @@
/* Copyright (C) 2005 Martin Koegler
* Copyright (C) 2006 OCCAM Financial Technology
* Copyright (C) 2010 TigerVNC Team
*
* 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.
*/
#ifndef __RFB_CSECURITYSTACK_H__
#define __RFB_CSECURITYSTACK_H__
#include <rfb/CSecurity.h>
#include <rfb/Security.h>
namespace rfb {
class CSecurityStack : public CSecurity {
public:
CSecurityStack(int Type, const char *Name, CSecurity* s0 = 0, CSecurity* s1 = 0);
~CSecurityStack();
virtual bool processMsg(CConnection* cc);
virtual int getType() const {return type;};
virtual const char* description() const {return name;}
virtual bool isSecure() const;
protected:
int state;
CSecurity* state0;
CSecurity* state1;
const char* name;
int type;
};
}
#endif

445
common/rfb/CSecurityTLS.cxx Normal file
View File

@@ -0,0 +1,445 @@
/*
* Copyright (C) 2004 Red Hat Inc.
* Copyright (C) 2005 Martin Koegler
* Copyright (C) 2010 TigerVNC Team
* Copyright (C) 2010 m-privacy GmbH
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef HAVE_GNUTLS
#error "This header should not be compiled without HAVE_GNUTLS defined"
#endif
#include <stdlib.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <rfb/CSecurityTLS.h>
#include <rfb/SSecurityVeNCrypt.h>
#include <rfb/CConnection.h>
#include <rfb/LogWriter.h>
#include <rfb/Exception.h>
#include <rfb/UserMsgBox.h>
#include <rdr/TLSInStream.h>
#include <rdr/TLSOutStream.h>
#include <os/os.h>
#include <gnutls/x509.h>
/*
* GNUTLS 2.6.5 and older didn't have some variables defined so don't use them.
* GNUTLS 1.X.X defined LIBGNUTLS_VERSION_NUMBER so treat it as "old" gnutls as
* well
*/
#if (defined(GNUTLS_VERSION_NUMBER) && GNUTLS_VERSION_NUMBER < 0x020606) || \
defined(LIBGNUTLS_VERSION_NUMBER)
#define WITHOUT_X509_TIMES
#endif
/* Ancient GNUTLS... */
#if !defined(GNUTLS_VERSION_NUMBER) && !defined(LIBGNUTLS_VERSION_NUMBER)
#define WITHOUT_X509_TIMES
#endif
using namespace rfb;
StringParameter CSecurityTLS::X509CA("X509CA", "X509 CA certificate", "", ConfViewer);
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)
{
cafile = X509CA.getData();
crlfile = X509CRL.getData();
if (gnutls_global_init() != GNUTLS_E_SUCCESS)
throw AuthFailureException("gnutls_global_init failed");
}
void CSecurityTLS::setDefaults()
{
char* homeDir = NULL;
if (getvnchomedir(&homeDir) == -1) {
vlog.error("Could not obtain VNC home directory path");
return;
}
int len = strlen(homeDir) + 1;
CharArray caDefault(len + 11);
CharArray crlDefault(len + 12);
sprintf(caDefault.buf, "%sx509_ca.pem", homeDir);
sprintf(crlDefault.buf, "%s509_crl.pem", homeDir);
delete [] homeDir;
if (!fileexists(caDefault.buf))
X509CA.setDefaultStr(strdup(caDefault.buf));
if (!fileexists(crlDefault.buf))
X509CRL.setDefaultStr(strdup(crlDefault.buf));
}
void CSecurityTLS::shutdown(bool needbye)
{
if (session && needbye)
if (gnutls_bye(session, GNUTLS_SHUT_RDWR) != GNUTLS_E_SUCCESS)
vlog.error("gnutls_bye failed");
if (anon_cred) {
gnutls_anon_free_client_credentials(anon_cred);
anon_cred = 0;
}
if (cert_cred) {
gnutls_certificate_free_credentials(cert_cred);
cert_cred = 0;
}
if (session) {
gnutls_deinit(session);
session = 0;
}
}
CSecurityTLS::~CSecurityTLS()
{
shutdown(true);
if (fis)
delete fis;
if (fos)
delete fos;
delete[] cafile;
delete[] crlfile;
gnutls_global_deinit();
}
bool CSecurityTLS::processMsg(CConnection* cc)
{
rdr::InStream* is = cc->getInStream();
rdr::OutStream* os = cc->getOutStream();
client = cc;
if (!session) {
if (!is->checkNoWait(1))
return false;
if (is->readU8() == 0) {
rdr::U32 result = is->readU32();
CharArray reason;
if (result == secResultFailed || result == secResultTooMany)
reason.buf = is->readString();
else
reason.buf = strDup("protocol error");
throw AuthFailureException(reason.buf);
}
if (gnutls_init(&session, GNUTLS_CLIENT) != GNUTLS_E_SUCCESS)
throw AuthFailureException("gnutls_init failed");
if (gnutls_set_default_priority(session) != GNUTLS_E_SUCCESS)
throw AuthFailureException("gnutls_set_default_priority failed");
setParam();
}
rdr::TLSInStream *tlsis = new rdr::TLSInStream(is, session);
rdr::TLSOutStream *tlsos = new rdr::TLSOutStream(os, session);
int err;
err = gnutls_handshake(session);
if (err != GNUTLS_E_SUCCESS) {
delete tlsis;
delete tlsos;
if (!gnutls_error_is_fatal(err))
return false;
vlog.error("TLS Handshake failed: %s\n", gnutls_strerror (err));
shutdown(false);
throw AuthFailureException("TLS Handshake failed");
}
checkSession();
cc->setStreams(fis = tlsis, fos = tlsos);
return true;
}
void CSecurityTLS::setParam()
{
static const char kx_anon_priority[] = ":+ANON-ECDH:+ANON-DH";
int ret;
char *prio;
const char *err;
prio = (char*)malloc(strlen(Security::GnuTLSPriority) +
strlen(kx_anon_priority) + 1);
if (prio == NULL)
throw AuthFailureException("Not enough memory for GnuTLS priority string");
strcpy(prio, Security::GnuTLSPriority);
if (anon)
strcat(prio, kx_anon_priority);
ret = gnutls_priority_set_direct(session, prio, &err);
free(prio);
if (ret != GNUTLS_E_SUCCESS) {
if (ret == GNUTLS_E_INVALID_REQUEST)
vlog.error("GnuTLS priority syntax error at: %s", err);
throw AuthFailureException("gnutls_set_priority_direct failed");
}
if (anon) {
if (gnutls_anon_allocate_client_credentials(&anon_cred) != GNUTLS_E_SUCCESS)
throw AuthFailureException("gnutls_anon_allocate_client_credentials failed");
if (gnutls_credentials_set(session, GNUTLS_CRD_ANON, anon_cred) != GNUTLS_E_SUCCESS)
throw AuthFailureException("gnutls_credentials_set failed");
vlog.debug("Anonymous session has been set");
} else {
if (gnutls_certificate_allocate_credentials(&cert_cred) != GNUTLS_E_SUCCESS)
throw AuthFailureException("gnutls_certificate_allocate_credentials failed");
if (*cafile && gnutls_certificate_set_x509_trust_file(cert_cred,cafile,GNUTLS_X509_FMT_PEM) < 0)
throw AuthFailureException("load of CA cert failed");
/* Load previously saved certs */
char *homeDir = NULL;
int err;
if (getvnchomedir(&homeDir) == -1)
vlog.error("Could not obtain VNC home directory path");
else {
CharArray caSave(strlen(homeDir) + 19 + 1);
sprintf(caSave.buf, "%sx509_savedcerts.pem", homeDir);
delete [] homeDir;
err = gnutls_certificate_set_x509_trust_file(cert_cred, caSave.buf,
GNUTLS_X509_FMT_PEM);
if (err < 0)
vlog.debug("Failed to load saved server certificates from %s", caSave.buf);
}
if (*crlfile && gnutls_certificate_set_x509_crl_file(cert_cred,crlfile,GNUTLS_X509_FMT_PEM) < 0)
throw AuthFailureException("load of CRL failed");
if (gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cert_cred) != GNUTLS_E_SUCCESS)
throw AuthFailureException("gnutls_credentials_set failed");
if (gnutls_server_name_set(session, GNUTLS_NAME_DNS,
client->getServerName(),
strlen(client->getServerName())) != GNUTLS_E_SUCCESS)
vlog.error("Failed to configure the server name for TLS handshake");
vlog.debug("X509 session has been set");
}
}
void CSecurityTLS::checkSession()
{
const unsigned allowed_errors = GNUTLS_CERT_INVALID |
GNUTLS_CERT_SIGNER_NOT_FOUND |
GNUTLS_CERT_SIGNER_NOT_CA;
unsigned int status;
const gnutls_datum_t *cert_list;
unsigned int cert_list_size = 0;
int err;
gnutls_datum_t info;
if (anon)
return;
if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509)
throw AuthFailureException("unsupported certificate type");
err = gnutls_certificate_verify_peers2(session, &status);
if (err != 0) {
vlog.error("server certificate verification failed: %s", gnutls_strerror(err));
throw AuthFailureException("server certificate verification failed");
}
if (status & GNUTLS_CERT_REVOKED)
throw AuthFailureException("server certificate has been revoked");
#ifndef WITHOUT_X509_TIMES
if (status & GNUTLS_CERT_NOT_ACTIVATED)
throw AuthFailureException("server certificate has not been activated");
if (status & GNUTLS_CERT_EXPIRED) {
vlog.debug("server certificate has expired");
if (!msg->showMsgBox(UserMsgBox::M_YESNO, "certificate has expired",
"The certificate of the server has expired, "
"do you want to continue?"))
throw AuthFailureException("server certificate has expired");
}
#endif
/* Process other errors later */
cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
if (!cert_list_size)
throw AuthFailureException("empty certificate chain");
/* Process only server's certificate, not issuer's certificate */
gnutls_x509_crt_t crt;
gnutls_x509_crt_init(&crt);
if (gnutls_x509_crt_import(crt, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
throw AuthFailureException("decoding of certificate failed");
if (gnutls_x509_crt_check_hostname(crt, client->getServerName()) == 0) {
char buf[255];
vlog.debug("hostname mismatch");
snprintf(buf, sizeof(buf), "Hostname (%s) does not match any certificate, "
"do you want to continue?", client->getServerName());
buf[sizeof(buf) - 1] = '\0';
if (!msg->showMsgBox(UserMsgBox::M_YESNO, "hostname mismatch", buf))
throw AuthFailureException("hostname mismatch");
}
if (status == 0) {
/* Everything is fine (hostname + verification) */
gnutls_x509_crt_deinit(crt);
return;
}
if (status & GNUTLS_CERT_INVALID)
vlog.debug("server certificate invalid");
if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
vlog.debug("server cert signer not found");
if (status & GNUTLS_CERT_SIGNER_NOT_CA)
vlog.debug("server cert signer not CA");
if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
throw AuthFailureException("The server certificate uses an insecure algorithm");
if ((status & (~allowed_errors)) != 0) {
/* No other errors are allowed */
vlog.debug("GNUTLS status of certificate verification: %u", status);
throw AuthFailureException("Invalid status of server certificate verification");
}
vlog.debug("Saved server certificates don't match");
if (gnutls_x509_crt_print(crt, GNUTLS_CRT_PRINT_ONELINE, &info)) {
/*
* GNUTLS doesn't correctly export gnutls_free symbol which is
* a function pointer. Linking with Visual Studio 2008 Express will
* fail when you call gnutls_free().
*/
#if WIN32
free(info.data);
#else
gnutls_free(info.data);
#endif
throw AuthFailureException("Could not find certificate to display");
}
size_t out_size = 0;
char *out_buf = NULL;
char *certinfo = NULL;
int len = 0;
vlog.debug("certificate issuer unknown");
len = snprintf(NULL, 0, "This certificate has been signed by an unknown "
"authority:\n\n%s\n\nDo you want to save it and "
"continue?\n ", info.data);
if (len < 0)
AuthFailureException("certificate decoding error");
vlog.debug("%s", info.data);
certinfo = new char[len];
if (certinfo == NULL)
throw AuthFailureException("Out of memory");
snprintf(certinfo, len, "This certificate has been signed by an unknown "
"authority:\n\n%s\n\nDo you want to save it and "
"continue? ", info.data);
for (int i = 0; i < len - 1; i++)
if (certinfo[i] == ',' && certinfo[i + 1] == ' ')
certinfo[i] = '\n';
if (!msg->showMsgBox(UserMsgBox::M_YESNO, "certificate issuer unknown",
certinfo)) {
delete [] certinfo;
throw AuthFailureException("certificate issuer unknown");
}
delete [] certinfo;
if (gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, NULL, &out_size)
== GNUTLS_E_SHORT_MEMORY_BUFFER)
AuthFailureException("Out of memory");
// Save cert
out_buf = new char[out_size];
if (out_buf == NULL)
AuthFailureException("Out of memory");
if (gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, out_buf, &out_size) < 0)
AuthFailureException("certificate issuer unknown, and certificate "
"export failed");
char *homeDir = NULL;
if (getvnchomedir(&homeDir) == -1)
vlog.error("Could not obtain VNC home directory path");
else {
FILE *f;
CharArray caSave(strlen(homeDir) + 1 + 19);
sprintf(caSave.buf, "%sx509_savedcerts.pem", homeDir);
delete [] homeDir;
f = fopen(caSave.buf, "a+");
if (!f)
msg->showMsgBox(UserMsgBox::M_OK, "certificate save failed",
"Could not save the certificate");
else {
fprintf(f, "%s\n", out_buf);
fclose(f);
}
}
delete [] out_buf;
gnutls_x509_crt_deinit(crt);
/*
* GNUTLS doesn't correctly export gnutls_free symbol which is
* a function pointer. Linking with Visual Studio 2008 Express will
* fail when you call gnutls_free().
*/
#if WIN32
free(info.data);
#else
gnutls_free(info.data);
#endif
}

77
common/rfb/CSecurityTLS.h Normal file
View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2004 Red Hat Inc.
* Copyright (C) 2005 Martin Koegler
* Copyright (C) 2010 TigerVNC Team
*
* 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.
*/
#ifndef __C_SECURITY_TLS_H__
#define __C_SECURITY_TLS_H__
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef HAVE_GNUTLS
#error "This header should not be compiled without HAVE_GNUTLS defined"
#endif
#include <rfb/CSecurity.h>
#include <rfb/SSecurityVeNCrypt.h>
#include <rfb/Security.h>
#include <rfb/UserMsgBox.h>
#include <rdr/InStream.h>
#include <rdr/OutStream.h>
#include <gnutls/gnutls.h>
namespace rfb {
class UserMsgBox;
class CSecurityTLS : public CSecurity {
public:
CSecurityTLS(bool _anon);
virtual ~CSecurityTLS();
virtual bool processMsg(CConnection* cc);
virtual int getType() const { return anon ? secTypeTLSNone : secTypeX509None; }
virtual const char* description() const
{ return anon ? "TLS Encryption without VncAuth" : "X509 Encryption without VncAuth"; }
virtual bool isSecure() const { return !anon; }
static void setDefaults();
static StringParameter X509CA;
static StringParameter X509CRL;
static UserMsgBox *msg;
protected:
void shutdown(bool needbye);
void freeResources();
void setParam();
void checkSession();
CConnection *client;
private:
gnutls_session_t session;
gnutls_anon_client_credentials_t anon_cred;
gnutls_certificate_credentials_t cert_cred;
bool anon;
char *cafile, *crlfile;
rdr::InStream* fis;
rdr::OutStream* fos;
};
}
#endif

View File

@@ -0,0 +1,206 @@
/*
* Copyright (C) 2006 OCCAM Financial Technology
* Copyright (C) 2005-2006 Martin Koegler
* Copyright (C) 2010 TigerVNC Team
*
* 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.
*/
//
// CSecurityVeNCrypt
//
#include <rfb/Exception.h>
#include <rdr/InStream.h>
#include <rdr/OutStream.h>
#include <rfb/CConnection.h>
#include <rfb/CSecurityVeNCrypt.h>
#include <rfb/LogWriter.h>
#include <list>
using namespace rfb;
using namespace rdr;
using namespace std;
static LogWriter vlog("CVeNCrypt");
CSecurityVeNCrypt::CSecurityVeNCrypt(SecurityClient* sec) : csecurity(NULL), security(sec)
{
haveRecvdMajorVersion = false;
haveRecvdMinorVersion = false;
haveSentVersion = false;
haveAgreedVersion = false;
haveListOfTypes = false;
haveNumberOfTypes = false;
haveChosenType = false;
majorVersion = 0;
minorVersion = 0;
chosenType = secTypeVeNCrypt;
nAvailableTypes = 0;
availableTypes = NULL;
iAvailableType = 0;
}
CSecurityVeNCrypt::~CSecurityVeNCrypt()
{
if (availableTypes)
delete[] availableTypes;
}
bool CSecurityVeNCrypt::processMsg(CConnection* cc)
{
InStream* is = cc->getInStream();
OutStream* os = cc->getOutStream();
/* get major, minor versions, send what we can support (or 0.0 for can't support it) */
if (!haveRecvdMajorVersion) {
majorVersion = is->readU8();
haveRecvdMajorVersion = true;
return false;
}
if (!haveRecvdMinorVersion) {
minorVersion = is->readU8();
haveRecvdMinorVersion = true;
}
/* major version in upper 8 bits and minor version in lower 8 bits */
U16 Version = (((U16) majorVersion) << 8) | ((U16) minorVersion);
if (!haveSentVersion) {
/* Currently we don't support former VeNCrypt 0.1 */
if (Version >= 0x0002) {
majorVersion = 0;
minorVersion = 2;
os->writeU8(majorVersion);
os->writeU8(minorVersion);
os->flush();
} else {
/* Send 0.0 to indicate no support */
majorVersion = 0;
minorVersion = 0;
os->writeU8(0);
os->writeU8(0);
os->flush();
throw AuthFailureException("The server reported an unsupported VeNCrypt version");
}
haveSentVersion = true;
return false;
}
/* Check that the server is OK */
if (!haveAgreedVersion) {
if (is->readU8())
throw AuthFailureException("The server reported it could not support the "
"VeNCrypt version");
haveAgreedVersion = true;
return false;
}
/* get a number of types */
if (!haveNumberOfTypes) {
nAvailableTypes = is->readU8();
iAvailableType = 0;
if (!nAvailableTypes)
throw AuthFailureException("The server reported no VeNCrypt sub-types");
availableTypes = new rdr::U32[nAvailableTypes];
haveNumberOfTypes = true;
return false;
}
if (nAvailableTypes) {
/* read in the types possible */
if (!haveListOfTypes) {
if (is->checkNoWait(4)) {
availableTypes[iAvailableType++] = is->readU32();
haveListOfTypes = (iAvailableType >= nAvailableTypes);
vlog.debug("Server offers security type %s (%d)",
secTypeName(availableTypes[iAvailableType - 1]),
availableTypes[iAvailableType - 1]);
if (!haveListOfTypes)
return false;
} else
return false;
}
/* make a choice and send it to the server, meanwhile set up the stack */
if (!haveChosenType) {
chosenType = secTypeInvalid;
U8 i;
list<U32>::iterator j;
list<U32> secTypes;
secTypes = security->GetEnabledExtSecTypes();
/* Honor server's security type order */
for (i = 0; i < nAvailableTypes; i++) {
for (j = secTypes.begin(); j != secTypes.end(); j++) {
if (*j == availableTypes[i]) {
chosenType = *j;
break;
}
}
if (chosenType != secTypeInvalid)
break;
}
vlog.info("Choosing security type %s (%d)", secTypeName(chosenType),
chosenType);
/* Set up the stack according to the chosen type: */
if (chosenType == secTypeInvalid || chosenType == secTypeVeNCrypt)
throw AuthFailureException("No valid VeNCrypt sub-type");
csecurity = security->GetCSecurity(chosenType);
/* send chosen type to server */
os->writeU32(chosenType);
os->flush();
haveChosenType = true;
}
} else {
/*
* Server told us that there are 0 types it can support - this should not
* happen, since if the server supports 0 sub-types, it doesn't support
* this security type
*/
throw AuthFailureException("The server reported 0 VeNCrypt sub-types");
}
return csecurity->processMsg(cc);
}
const char* CSecurityVeNCrypt::description() const
{
if (csecurity)
return csecurity->description();
return "VeNCrypt";
}
bool CSecurityVeNCrypt::isSecure() const
{
if (csecurity && csecurity->isSecure())
return true;
return false;
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2005-2006 Martin Koegler
* Copyright (C) 2006 OCCAM Financial Technology
* Copyright (C) 2010 TigerVNC Team
*
* 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.
*/
//
// CSecurityVeNCrypt
//
#ifndef __CSECURITYVENCRYPT_H__
#define __CSECURITYVENCRYPT_H__
#include <rfb/CSecurity.h>
#include <rfb/SecurityClient.h>
#include <rdr/types.h>
namespace rfb {
class CSecurityVeNCrypt : public CSecurity {
public:
CSecurityVeNCrypt(SecurityClient* sec);
~CSecurityVeNCrypt();
virtual bool processMsg(CConnection* cc);// { return true; }
int getType() const {return chosenType;}
virtual const char* description() const;
virtual bool isSecure() const;
protected:
CSecurity *csecurity;
SecurityClient *security;
bool haveRecvdMajorVersion;
bool haveRecvdMinorVersion;
bool haveSentVersion;
bool haveAgreedVersion;
bool haveListOfTypes;
bool haveNumberOfTypes;
bool haveChosenType;
rdr::U8 majorVersion, minorVersion;
rdr::U32 chosenType;
rdr::U8 nAvailableTypes;
rdr::U32 *availableTypes;
rdr::U8 iAvailableType;
};
}
#endif

View File

@@ -0,0 +1,67 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
//
// CSecurityVncAuth
//
// XXX not thread-safe, because d3des isn't - do we need to worry about this?
//
#include <string.h>
#include <stdio.h>
#include <rfb/CConnection.h>
#include <rfb/Password.h>
#include <rfb/CSecurityVncAuth.h>
#include <rfb/util.h>
#include <rfb/Security.h>
extern "C" {
#include <rfb/d3des.h>
}
#include <rdr/InStream.h>
#include <rdr/OutStream.h>
using namespace rfb;
static const int vncAuthChallengeSize = 16;
bool CSecurityVncAuth::processMsg(CConnection* cc)
{
rdr::InStream* is = cc->getInStream();
rdr::OutStream* os = cc->getOutStream();
// Read the challenge & obtain the user's password
rdr::U8 challenge[vncAuthChallengeSize];
is->readBytes(challenge, vncAuthChallengeSize);
PlainPasswd passwd;
(CSecurity::upg)->getUserPasswd(cc->isSecure(), 0, &passwd.buf);
// Calculate the correct response
rdr::U8 key[8];
int pwdLen = strlen(passwd.buf);
for (int i=0; i<8; i++)
key[i] = i<pwdLen ? passwd.buf[i] : 0;
deskey(key, EN0);
for (int j = 0; j < vncAuthChallengeSize; j += 8)
des(challenge+j, challenge+j);
// Return the response to the server
os->writeBytes(challenge, vncAuthChallengeSize);
os->flush();
return true;
}

View File

@@ -0,0 +1,35 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
#ifndef __RFB_CSECURITYVNCAUTH_H__
#define __RFB_CSECURITYVNCAUTH_H__
#include <rfb/CSecurity.h>
#include <rfb/Security.h>
namespace rfb {
class CSecurityVncAuth : public CSecurity {
public:
CSecurityVncAuth(void) {}
virtual ~CSecurityVncAuth() {}
virtual bool processMsg(CConnection* cc);
virtual int getType() const {return secTypeVncAuth;};
virtual const char* description() const {return "No Encryption";}
};
}
#endif

View File

@@ -0,0 +1,986 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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 <assert.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include <zlib.h>
#include <rdr/types.h>
#include <rfb/Exception.h>
#include <rfb/LogWriter.h>
#include <rfb/ServerCore.h>
#include <rfb/ComparingUpdateTracker.h>
#include <rfb/adler32.h>
#include <rfb/xxhash.h>
using namespace rfb;
static LogWriter vlog("ComparingUpdateTracker");
static uint32_t ispow(const uint32_t in) {
if (in < 2) return 0;
return !(in & (in - 1));
}
static uint32_t npow(uint32_t in) {
if (ispow(in)) return in;
in |= in >> 1;
in |= in >> 2;
in |= in >> 4;
in |= in >> 8;
in |= in >> 16;
return in + 1;
}
static uint32_t pow2shift(const uint32_t in) {
return __builtin_ffs(in) - 1;
}
#define SCROLLBLOCK_SIZE 64
#define NUM_TOTALS (1024 * 256)
#define MAX_CHECKS 8
class scrollHasher_t {
protected:
struct hashdata_t {
uint32_t hash;
bool operator ==(const hashdata_t &other) const {
return hash == other.hash;
}
bool operator <(const hashdata_t &other) const {
return hash < other.hash;
}
};
struct hasher {
size_t operator()(const hashdata_t &in) const {
return in.hash;
}
};
struct match_t {
uint32_t hash, idx;
};
uint_fast32_t w, h, d, lineBytes, blockBytes;
hashdata_t *hashtable;
uint_fast32_t hashw, hashAnd, hashShift;
mutable int_fast16_t lastOffX, lastOffY;
const uint8_t *olddata;
uint32_t *totals, *starts, *idxtable, *curs;
public:
scrollHasher_t(): w(0), h(0), d(0), lineBytes(0), blockBytes(0), hashtable(NULL),
hashw(0), hashAnd(0), hashShift(0),
lastOffX(0), lastOffY(0),
olddata(NULL), totals(NULL), starts(NULL), idxtable(NULL) {
assert(sizeof(hashdata_t) == sizeof(uint32_t));
}
virtual ~scrollHasher_t() {
free(totals);
free(starts);
free(curs);
free(hashtable);
free(idxtable);
free((void *) olddata);
}
virtual void calcHashes(const uint8_t *ptr,
const uint32_t w_, const uint32_t h_, const uint32_t d_) = 0;
virtual void invalidate(const uint_fast32_t x, uint_fast32_t y, uint_fast32_t h) = 0;
virtual void findBestMatch(const uint8_t * const ptr, const uint_fast32_t maxLines,
const uint_fast32_t inx, const uint_fast32_t iny,
uint_fast32_t *outx,
uint_fast32_t *outy,
uint_fast32_t *outlines) const = 0;
virtual void findBlock(const uint8_t * const ptr,
const uint_fast32_t inx, const uint_fast32_t iny,
uint_fast32_t *outx,
uint_fast32_t *outy,
uint_fast32_t *outlines) const = 0;
};
class scrollHasher_vert_t: public scrollHasher_t {
public:
scrollHasher_vert_t(): scrollHasher_t() {
totals = (uint32_t *) malloc(sizeof(uint32_t) * NUM_TOTALS);
starts = (uint32_t *) malloc(sizeof(uint32_t) * NUM_TOTALS);
curs = (uint32_t *) malloc(sizeof(uint32_t) * NUM_TOTALS);
}
void calcHashes(const uint8_t *ptr,
const uint32_t w_, const uint32_t h_, const uint32_t d_) {
if (w != w_ || h != h_) {
// Reallocate
w = w_;
h = h_;
d = d_;
lineBytes = w * d;
blockBytes = SCROLLBLOCK_SIZE * d;
hashw = npow(w / SCROLLBLOCK_SIZE);
hashAnd = hashw - 1;
hashShift = pow2shift(hashw);
hashtable = (hashdata_t *) realloc(hashtable,
hashw * h * sizeof(uint32_t));
idxtable = (uint32_t *) realloc(idxtable,
hashw * h * sizeof(uint32_t));
olddata = (const uint8_t *) realloc((void *) olddata, w * h * d);
}
// We need to make a copy, since the comparer incrementally updates its copy
memcpy((uint8_t *) olddata, ptr, w * h * d);
//memset(hashtable, 0, hashw * h * sizeof(uint32_t));
//memset(idxtable, 0, w * h * sizeof(uint32_t));
memset(totals, 0, NUM_TOTALS * sizeof(uint32_t));
//memset(starts, 0, NUM_TOTALS * sizeof(uint32_t));
//memset(curs, 0, NUM_TOTALS * sizeof(uint32_t));
for (uint_fast32_t y = 0; y < h; y++) {
const uint8_t *inptr0 = olddata;
inptr0 += y * lineBytes;
for (uint_fast32_t x = 0; x < w; x += SCROLLBLOCK_SIZE) {
if (w - x < SCROLLBLOCK_SIZE)
break;
const uint_fast32_t idx = (y << hashShift) + x / SCROLLBLOCK_SIZE;
hashtable[idx].hash = XXH64(inptr0, blockBytes, 0);
totals[hashtable[idx].hash % NUM_TOTALS]++;
inptr0 += blockBytes;
}
}
// calculate number of unique 21-bit hashes
/*uint_fast32_t uniqHashes = 0;
for (uint_fast32_t i = 0; i < NUM_TOTALS; i++) {
if (totals[i])
uniqHashes++;
}
printf("%lu unique hashes\n", uniqHashes);*/
// Update starting positions
uint_fast32_t sum = 0;
for (uint_fast32_t i = 0; i < NUM_TOTALS; i++) {
if (!totals[i])
continue;
starts[i] = curs[i] = sum;
sum += totals[i];
}
// update index table
const hashdata_t *src = hashtable;
for (uint_fast32_t y = 0; y < h; y++) {
uint_fast32_t ybase = (y << hashShift);
for (uint_fast32_t x = 0; x < w; x += SCROLLBLOCK_SIZE, ybase++) {
if (w - x < SCROLLBLOCK_SIZE)
break;
const uint_fast32_t val = src[x / SCROLLBLOCK_SIZE].hash;
const uint_fast32_t smallIdx = val % NUM_TOTALS;
const uint_fast32_t newpos = curs[smallIdx]++;
// this assert is very heavy, uncomment only for debugging
//assert(curs[smallIdx] - starts[smallIdx] <= totals[smallIdx]);
idxtable[newpos] = ybase;
}
src += hashw;
}
lastOffX = lastOffY = 0;
}
void invalidate(const uint_fast32_t x, uint_fast32_t y, uint_fast32_t h) {
h += y;
for (; y < h; y++) {
memset(&hashtable[(y << hashShift) + x / SCROLLBLOCK_SIZE], 0,
sizeof(uint32_t));
}
}
void findBestMatch(const uint8_t * const ptr, const uint_fast32_t maxLines,
const uint_fast32_t inx, const uint_fast32_t iny,
uint_fast32_t *outx,
uint_fast32_t *outy,
uint_fast32_t *outlines) const {
const uint_fast32_t starthash = (uint32_t) XXH64(ptr, blockBytes, 0);
const uint_fast32_t smallIdx = starthash % NUM_TOTALS;
match_t matches[MAX_CHECKS];
*outlines = 0;
uint_fast32_t i, upto, curidx, curhash, found = 0, inc;
upto = totals[smallIdx] + starts[smallIdx];
if (!totals[smallIdx])
return;
inc = totals[smallIdx] / 32;
if (!inc)
inc = 1;
//printf("target hash %lx, it has %u matches\n",
// starthash, totals[smallIdx]);
// First, try the last good offset. If this was a scroll,
// and we have a good offset, it should match almost everything
const uint_fast16_t tryX = inx + lastOffX;
const uint_fast16_t tryY = iny + lastOffY;
if ((lastOffX || lastOffY) &&
tryX < w - (SCROLLBLOCK_SIZE - 1) &&
tryY < h - maxLines) {
//printf("Trying good offset %ld,%ld for in %lu,%lu, try %lu,%lu\n",
// lastOffX, lastOffY, inx, iny, tryX, tryY);
curidx = (tryY << hashShift) + tryX / SCROLLBLOCK_SIZE;
curhash = hashtable[curidx].hash;
if (curhash == starthash &&
memcmp(ptr, &olddata[tryY * lineBytes + tryX * d], blockBytes) == 0) {
matches[0].hash = curhash;
matches[0].idx = curidx;
found++;
} /*else printf("Nope, hashes %u %lx %lx, mem %u, maxlines %lu\n",
curhash == starthash, curhash, starthash,
memcmp(ptr, &olddata[tryY * lineBytes + tryX * d], blockBytes) == 0,
maxLines);*/
}
for (i = starts[smallIdx]; i < upto; i += inc) {
curidx = idxtable[i];
curhash = hashtable[curidx].hash;
if (curhash != starthash)
continue;
// Convert to olddata position
const uint_fast32_t oldy = curidx >> hashShift;
const uint_fast32_t oldx = curidx & hashAnd;
if (memcmp(ptr, &olddata[oldy * lineBytes + oldx * blockBytes], blockBytes))
continue;
matches[found].hash = curhash;
matches[found].idx = curidx;
found++;
if (found >= MAX_CHECKS)
break;
}
if (!found)
return;
//printf("%lu of those were suitable for further checks\n", found);
// Find best of them
uint_fast32_t best = 0, bestmatches = 0;
for (i = 0; i < found; i++) {
const uint_fast32_t oldy = matches[i].idx >> hashShift;
const uint_fast32_t oldx = matches[i].idx & hashAnd;
uint_fast32_t k, bothMaxLines;
bothMaxLines = maxLines;
if (bothMaxLines > h - oldy)
bothMaxLines = h - oldy;
for (k = 1; k < bothMaxLines; k++) {
/* curhash = adler32(adler32(0, NULL, 0), ptr + lineBytes * k,
blockBytes);
if (curhash != hashtable[matches[i].idx + hashw * k].hash)
break;*/
if (!hashtable[matches[i].idx + (k << hashShift)].hash)
break; // Invalidated
if (memcmp(ptr + lineBytes * k,
&olddata[(oldy + k) * lineBytes + oldx * blockBytes],
blockBytes))
break;
}
if (k > bestmatches) {
bestmatches = k;
best = i;
}
if (k == maxLines)
break;
}
//printf("Best had %lu matching lines of allowed %lu\n", bestmatches, maxLines);
*outlines = bestmatches;
*outx = (matches[best].idx & hashAnd) * SCROLLBLOCK_SIZE;
*outy = matches[best].idx >> hashShift;
// Was it a good match? If so, store for later
if (*outx == inx && bestmatches >= maxLines / 2 &&
totals[smallIdx] < 4 && *outy != iny) {
lastOffX = 0;
lastOffY = *outy - iny;
}
}
void findBlock(const uint8_t * const ptr,
const uint_fast32_t inx, const uint_fast32_t iny,
uint_fast32_t *outx,
uint_fast32_t *outy,
uint_fast32_t *outlines) const {
uint_fast32_t i, lowest = 0, tmpx = 0, tmpy = 0, tmplines,
searchHash, lowestTotal = 10000, tmpTotal;
*outlines = 0;
for (i = 0; i < SCROLLBLOCK_SIZE; i++) {
searchHash = (uint32_t) XXH64(ptr + lineBytes * i, blockBytes, 0);
const uint_fast32_t smallIdx = searchHash % NUM_TOTALS;
tmpTotal = totals[smallIdx];
if (!tmpTotal)
return;
if (tmpTotal < lowestTotal) {
lowest = i;
lowestTotal = tmpTotal;
}
}
// If the lowest number of matches is too high, we probably can't find
// a full block
if (lowestTotal > MAX_CHECKS)
return;
//printf("Lowest was %lu, %lu totals\n", lowest, lowestTotal);
findBestMatch(ptr + lineBytes * lowest, SCROLLBLOCK_SIZE - lowest,
inx, iny + lowest, &tmpx, &tmpy, &tmplines);
// The end didn't match
if (tmplines != SCROLLBLOCK_SIZE - lowest)
return;
if (tmpx != inx)
return;
// Source too high?
if (tmpy < lowest)
return;
// Try to see if the beginning matches
for (i = 0; i < lowest; i++) {
if (!hashtable[((tmpy - lowest + i) << hashShift) + inx / SCROLLBLOCK_SIZE].hash)
return; // Invalidated
if (memcmp(ptr + lineBytes * i,
&olddata[(tmpy - lowest + i) * lineBytes + tmpx * d],
blockBytes))
return;
}
*outlines = 64;
*outx = tmpx;
*outy = tmpy - lowest;
}
};
#undef NUM_TOTALS
#define NUM_TOTALS (1024 * 1024 * 2)
class scrollHasher_bothDir_t: public scrollHasher_t {
public:
scrollHasher_bothDir_t(): scrollHasher_t() {
totals = (uint32_t *) malloc(sizeof(uint32_t) * NUM_TOTALS);
starts = (uint32_t *) malloc(sizeof(uint32_t) * NUM_TOTALS);
curs = (uint32_t *) malloc(sizeof(uint32_t) * NUM_TOTALS);
}
void calcHashes(const uint8_t *ptr,
const uint32_t w_, const uint32_t h_, const uint32_t d_) {
if (w != w_ || h != h_) {
// Reallocate
w = w_;
h = h_;
d = d_;
lineBytes = w * d;
blockBytes = SCROLLBLOCK_SIZE * d;
hashw = npow(w - (SCROLLBLOCK_SIZE - 1));
hashAnd = hashw - 1;
hashShift = pow2shift(hashw);
hashtable = (hashdata_t *) realloc(hashtable,
hashw * h * sizeof(uint32_t));
idxtable = (uint32_t *) realloc(idxtable,
w * h * sizeof(uint32_t));
olddata = (const uint8_t *) realloc((void *) olddata, w * h * d);
}
// We need to make a copy, since the comparer incrementally updates its copy
memcpy((uint8_t *) olddata, ptr, w * h * d);
Adler32 rolling(blockBytes);
//memset(hashtable, 0, hashw * h * sizeof(uint32_t));
//memset(idxtable, 0, w * h * sizeof(uint32_t));
memset(totals, 0, NUM_TOTALS * sizeof(uint32_t));
//memset(starts, 0, NUM_TOTALS * sizeof(uint32_t));
//memset(curs, 0, NUM_TOTALS * sizeof(uint32_t));
const uint8_t *prevptr = NULL;
for (uint_fast32_t y = 0; y < h; y++) {
const uint8_t *inptr0 = olddata;
inptr0 += y * lineBytes;
for (uint_fast32_t x = 0; x < w - (SCROLLBLOCK_SIZE - 1); x++) {
if (!x) {
rolling.reset();
uint_fast32_t g;
for (g = 0; g < SCROLLBLOCK_SIZE; g++) {
for (uint_fast32_t di = 0; di < d; di++) {
rolling.eat(inptr0[g * d + di]);
}
}
} else {
for (uint_fast32_t di = 0; di < d; di++) {
rolling.update(prevptr[di],
inptr0[(SCROLLBLOCK_SIZE - 1) * d + di]);
}
}
const uint_fast32_t idx = (y << hashShift) + x;
hashtable[idx].hash = rolling.hash;
totals[rolling.hash % NUM_TOTALS]++;
prevptr = inptr0;
inptr0 += d;
}
}
// calculate number of unique 21-bit hashes
/*uint_fast32_t uniqHashes = 0;
for (uint_fast32_t i = 0; i < NUM_TOTALS; i++) {
if (totals[i])
uniqHashes++;
}
printf("%lu unique hashes\n", uniqHashes);*/
// Update starting positions
uint_fast32_t sum = 0;
for (uint_fast32_t i = 0; i < NUM_TOTALS; i++) {
if (!totals[i])
continue;
starts[i] = curs[i] = sum;
sum += totals[i];
}
// update index table
const hashdata_t *src = hashtable;
for (uint_fast32_t y = 0; y < h; y++) {
uint_fast32_t ybase = (y << hashShift);
for (uint_fast32_t x = 0; x < w - (SCROLLBLOCK_SIZE - 1); x++, ybase++) {
const uint_fast32_t val = src[x].hash;
const uint_fast32_t smallIdx = val % NUM_TOTALS;
const uint_fast32_t newpos = curs[smallIdx]++;
// this assert is very heavy, uncomment only for debugging
//assert(curs[smallIdx] - starts[smallIdx] <= totals[smallIdx]);
idxtable[newpos] = ybase;
}
src += hashw;
}
lastOffX = lastOffY = 0;
}
void invalidate(const uint_fast32_t x, uint_fast32_t y, uint_fast32_t h) {
const uint_fast32_t nw = SCROLLBLOCK_SIZE;
const uint_fast32_t left = x > (SCROLLBLOCK_SIZE - 1) ?
(SCROLLBLOCK_SIZE - 1) : x;
const uint_fast32_t right = x + nw + (SCROLLBLOCK_SIZE - 1) < w ?
(SCROLLBLOCK_SIZE - 1) : w - x - nw;
h += y;
for (; y < h; y++) {
memset(&hashtable[(y << hashShift) + x - left], 0,
sizeof(uint32_t) * (nw + left + right));
}
}
void findBestMatch(const uint8_t * const ptr, const uint_fast32_t maxLines,
const uint_fast32_t inx, const uint_fast32_t iny,
uint_fast32_t *outx,
uint_fast32_t *outy,
uint_fast32_t *outlines) const {
const uint_fast32_t starthash = adler32(adler32(0, NULL, 0), ptr,
blockBytes);
const uint_fast32_t smallIdx = starthash % NUM_TOTALS;
match_t matches[MAX_CHECKS];
*outlines = 0;
uint_fast32_t i, upto, curidx, curhash, found = 0, inc;
upto = totals[smallIdx] + starts[smallIdx];
if (!totals[smallIdx])
return;
inc = totals[smallIdx] / 32;
if (!inc)
inc = 1;
//printf("target hash %lx, it has %u matches\n",
// starthash, totals[smallIdx]);
// First, try the last good offset. If this was a scroll,
// and we have a good offset, it should match almost everything
const uint_fast16_t tryX = inx + lastOffX;
const uint_fast16_t tryY = iny + lastOffY;
if ((lastOffX || lastOffY) &&
tryX < w - (SCROLLBLOCK_SIZE - 1) &&
tryY < h - maxLines) {
//printf("Trying good offset %ld,%ld for in %lu,%lu, try %lu,%lu\n",
// lastOffX, lastOffY, inx, iny, tryX, tryY);
curidx = (tryY << hashShift) + tryX;
curhash = hashtable[curidx].hash;
if (curhash == starthash &&
memcmp(ptr, &olddata[tryY * lineBytes + tryX * d], blockBytes) == 0) {
matches[0].hash = curhash;
matches[0].idx = curidx;
found++;
} /*else printf("Nope, hashes %u %lx %lx, mem %u, maxlines %lu\n",
curhash == starthash, curhash, starthash,
memcmp(ptr, &olddata[tryY * lineBytes + tryX * d], blockBytes) == 0,
maxLines);*/
}
for (i = starts[smallIdx]; i < upto; i += inc) {
curidx = idxtable[i];
curhash = hashtable[curidx].hash;
if (curhash != starthash)
continue;
// Convert to olddata position
const uint_fast32_t oldy = curidx >> hashShift;
const uint_fast32_t oldx = curidx & hashAnd;
if (memcmp(ptr, &olddata[oldy * lineBytes + oldx * d], blockBytes))
continue;
matches[found].hash = curhash;
matches[found].idx = curidx;
found++;
if (found >= MAX_CHECKS)
break;
}
if (!found)
return;
//printf("%lu of those were suitable for further checks\n", found);
// Find best of them
uint_fast32_t best = 0, bestmatches = 0;
for (i = 0; i < found; i++) {
const uint_fast32_t oldy = matches[i].idx >> hashShift;
const uint_fast32_t oldx = matches[i].idx & hashAnd;
uint_fast32_t k, bothMaxLines;
bothMaxLines = maxLines;
if (bothMaxLines > h - oldy)
bothMaxLines = h - oldy;
for (k = 1; k < bothMaxLines; k++) {
/* curhash = adler32(adler32(0, NULL, 0), ptr + lineBytes * k,
blockBytes);
if (curhash != hashtable[matches[i].idx + hashw * k].hash)
break;*/
if (!hashtable[matches[i].idx + (k << hashShift)].hash)
break; // Invalidated
if (memcmp(ptr + lineBytes * k,
&olddata[(oldy + k) * lineBytes + oldx * d],
blockBytes))
break;
}
if (k > bestmatches) {
bestmatches = k;
best = i;
}
if (k == maxLines)
break;
}
//printf("Best had %lu matching lines of allowed %lu\n", bestmatches, maxLines);
*outlines = bestmatches;
*outx = matches[best].idx & hashAnd;
*outy = matches[best].idx >> hashShift;
// Was it a good match? If so, store for later
if (bestmatches >= maxLines / 2 &&
totals[smallIdx] < 4) {
lastOffX = *outx - inx;
lastOffY = *outy - iny;
}
}
void findBlock(const uint8_t * const ptr,
const uint_fast32_t inx, const uint_fast32_t iny,
uint_fast32_t *outx,
uint_fast32_t *outy,
uint_fast32_t *outlines) const {
*outlines = 0;
// Not implemented for horizontal
}
};
ComparingUpdateTracker::ComparingUpdateTracker(PixelBuffer* buffer)
: fb(buffer), oldFb(fb->getPF(), 0, 0), firstCompare(true),
enabled(true), detectScroll(false), totalPixels(0), missedPixels(0),
scrollHasher(NULL)
{
changed.assign_union(fb->getRect());
if (Server::detectHorizontal)
scrollHasher = new scrollHasher_bothDir_t;
else
scrollHasher = new scrollHasher_vert_t;
}
ComparingUpdateTracker::~ComparingUpdateTracker()
{
delete scrollHasher;
}
#define BLOCK_SIZE 64
bool ComparingUpdateTracker::compare(bool skipScrollDetection, const Region &skipCursorArea)
{
std::vector<Rect> rects;
std::vector<Rect>::iterator i;
if (!enabled)
return false;
if (firstCompare) {
// NB: We leave the change region untouched on this iteration,
// since in effect the entire framebuffer has changed.
oldFb.setSize(fb->width(), fb->height());
for (int y=0; y<fb->height(); y+=BLOCK_SIZE) {
Rect pos(0, y, fb->width(), __rfbmin(fb->height(), y+BLOCK_SIZE));
int srcStride;
const rdr::U8* srcData = fb->getBuffer(pos, &srcStride);
oldFb.imageRect(pos, srcData, srcStride);
}
firstCompare = false;
return false;
}
copied.get_rects(&rects, copy_delta.x<=0, copy_delta.y<=0);
for (i = rects.begin(); i != rects.end(); i++)
oldFb.copyRect(*i, copy_delta);
changed.get_rects(&rects);
uint32_t changedArea = 0;
bool atLeast64 = false;
detectScroll = false;
for (i = rects.begin(); i != rects.end(); i++) {
if (!atLeast64 && i->width() >= 64)
atLeast64 = true;
changedArea += i->area();
}
if (atLeast64 && Server::detectScrolling && !skipScrollDetection &&
(changedArea * 100) / (fb->width() * fb->height()) > (unsigned) Server::scrollDetectLimit) {
detectScroll = true;
Rect pos(0, 0, oldFb.width(), oldFb.height());
int unused;
scrollHasher->calcHashes(oldFb.getBuffer(pos, &unused), oldFb.width(), oldFb.height(),
oldFb.getPF().bpp / 8);
// Invalidating lossy areas is not needed, the lossy region tracking tracks copies too
}
copyPassRects.clear();
Region newChanged;
for (i = rects.begin(); i != rects.end(); i++)
compareRect(*i, &newChanged, skipCursorArea);
changed.get_rects(&rects);
for (i = rects.begin(); i != rects.end(); i++)
totalPixels += i->area();
newChanged.get_rects(&rects);
for (i = rects.begin(); i != rects.end(); i++)
missedPixels += i->area();
if (changed.equals(newChanged))
return false;
changed = newChanged;
return true;
}
void ComparingUpdateTracker::enable()
{
enabled = true;
}
void ComparingUpdateTracker::disable()
{
enabled = false;
// Make sure we update the framebuffer next time we get enabled
firstCompare = true;
}
static void tryMerge(std::vector<CopyPassRect> &copyPassRects,
const int y, const int blockLeft,
const int blockRight, const uint_fast32_t outlines,
const uint_fast32_t outx, const uint_fast32_t outy) {
if (copyPassRects.size() &&
copyPassRects.back().rect.tl.y == y &&
copyPassRects.back().rect.br.x == blockLeft &&
(unsigned) copyPassRects.back().rect.br.y == y + outlines &&
copyPassRects.back().src_x + copyPassRects.back().rect.width() == outx &&
copyPassRects.back().src_y == outy) {
CopyPassRect &prev = copyPassRects.back();
prev.rect.br.x += SCROLLBLOCK_SIZE;
//merged++;
} else {
// Before adding this new rect as a non-mergeable one, try to vertically merge the
// previous two
if (copyPassRects.size() > 1) {
CopyPassRect &prev = *(copyPassRects.end() - 2);
const CopyPassRect &cur = copyPassRects.back();
if (prev.rect.br.y == cur.rect.tl.y &&
prev.rect.tl.x == cur.rect.tl.x &&
prev.rect.br.x == cur.rect.br.x &&
prev.src_x == cur.src_x &&
prev.src_y + prev.rect.height() == cur.src_y) {
prev.rect.br.y += cur.rect.height();
copyPassRects.pop_back();
}
}
const CopyPassRect cp = {Rect(blockLeft, y, blockRight, y + outlines),
(unsigned) outx, (unsigned) outy};
copyPassRects.push_back(cp);
}
}
void ComparingUpdateTracker::compareRect(const Rect& inr, Region* newChanged,
const Region &skipCursorArea)
{
Rect r = inr;
if (detectScroll && !Server::detectHorizontal)
r.tl.x &= ~(BLOCK_SIZE - 1);
if (!r.enclosed_by(fb->getRect())) {
Rect safe;
// Crop the rect and try again
safe = r.intersect(fb->getRect());
if (!safe.is_empty())
compareRect(safe, newChanged, skipCursorArea);
return;
}
int bytesPerPixel = fb->getPF().bpp/8;
int oldStride;
rdr::U8* oldData = oldFb.getBufferRW(r, &oldStride);
int oldStrideBytes = oldStride * bytesPerPixel;
std::vector<Rect> changedBlocks;
for (int blockTop = r.tl.y; blockTop < r.br.y; blockTop += BLOCK_SIZE)
{
// Get a strip of the source buffer
Rect pos(r.tl.x, blockTop, r.br.x, __rfbmin(r.br.y, blockTop+BLOCK_SIZE));
int fbStride;
const rdr::U8* newBlockPtr = fb->getBuffer(pos, &fbStride);
int newStrideBytes = fbStride * bytesPerPixel;
rdr::U8* oldBlockPtr = oldData;
int blockBottom = __rfbmin(blockTop+BLOCK_SIZE, r.br.y);
for (int blockLeft = r.tl.x; blockLeft < r.br.x; blockLeft += BLOCK_SIZE)
{
const rdr::U8* newPtr = newBlockPtr;
rdr::U8* oldPtr = oldBlockPtr;
int blockRight = __rfbmin(blockLeft+BLOCK_SIZE, r.br.x);
int blockWidthInBytes = (blockRight-blockLeft) * bytesPerPixel;
bool changed = false;
int y;
for (y = blockTop; y < blockBottom; y++)
{
if (memcmp(oldPtr, newPtr, blockWidthInBytes) != 0)
{
// A block has changed - copy the remainder to the oldFb
changed = true;
const rdr::U8* savedPtr = newPtr;
for (int y2 = y; y2 < blockBottom; y2++)
{
memcpy(oldPtr, newPtr, blockWidthInBytes);
newPtr += newStrideBytes;
oldPtr += oldStrideBytes;
}
newPtr = savedPtr;
break;
}
newPtr += newStrideBytes;
oldPtr += oldStrideBytes;
}
if (!changed || (changed && !detectScroll) ||
(skipCursorArea.numRects() &&
!skipCursorArea.intersect(Rect(blockLeft, blockTop, blockRight, blockBottom)).is_empty())) {
if (changed || skipCursorArea.numRects())
changedBlocks.push_back(Rect(blockLeft, blockTop,
blockRight, blockBottom));
oldBlockPtr += blockWidthInBytes;
newBlockPtr += blockWidthInBytes;
continue;
}
uint_fast32_t outx, outy, outlines;
if (blockRight - blockLeft < SCROLLBLOCK_SIZE) {
// Block too small, put it out outright as changed
changedBlocks.push_back(Rect(blockLeft, blockTop,
blockRight, blockBottom));
} else {
// First, try to find a full block
outlines = 0;
if (blockBottom - blockTop == SCROLLBLOCK_SIZE)
scrollHasher->findBlock(newBlockPtr, blockLeft, blockTop, &outx, &outy,
&outlines);
if (outlines == SCROLLBLOCK_SIZE) {
// Perfect match!
// success += outlines;
tryMerge(copyPassRects, blockTop, blockLeft, blockRight, outlines, outx, outy);
scrollHasher->invalidate(blockLeft, blockTop, outlines);
oldBlockPtr += blockWidthInBytes;
newBlockPtr += blockWidthInBytes;
continue;
}
for (; y < blockBottom; y += outlines)
{
// We have the first changed line. Find the best match, if any
scrollHasher->findBestMatch(newPtr, blockBottom - y, blockLeft, y,
&outx, &outy, &outlines);
if (!outlines) {
// Heuristic, if a line did not match, probably
// the next few won't either
changedBlocks.push_back(Rect(blockLeft, y,
blockRight, __rfbmin(y + 4, blockBottom)));
y += 4;
newPtr += newStrideBytes * 4;
// unfound += 4;
continue;
}
// success += outlines;
// Try to merge it with the last rect
tryMerge(copyPassRects, y, blockLeft, blockRight, outlines, outx, outy);
scrollHasher->invalidate(blockLeft, y, outlines);
newPtr += newStrideBytes * outlines;
}
}
oldBlockPtr += blockWidthInBytes;
newBlockPtr += blockWidthInBytes;
}
oldData += oldStrideBytes * BLOCK_SIZE;
}
oldFb.commitBufferRW(r);
if (!changedBlocks.empty()) {
Region temp;
temp.setOrderedRects(changedBlocks);
newChanged->assign_union(temp);
}
}
void ComparingUpdateTracker::logStats()
{
double ratio;
char a[1024], b[1024];
siPrefix(totalPixels, "pixels", a, sizeof(a));
siPrefix(missedPixels, "pixels", b, sizeof(b));
ratio = (double)totalPixels / missedPixels;
vlog.info("%s in / %s out", a, b);
vlog.info("(1:%g ratio)", ratio);
totalPixels = missedPixels = 0;
}
void ComparingUpdateTracker::getUpdateInfo(UpdateInfo* info, const Region& cliprgn)
{
info->copypassed = copyPassRects;
SimpleUpdateTracker::getUpdateInfo(info, cliprgn);
}
void ComparingUpdateTracker::clear()
{
copyPassRects.clear();
SimpleUpdateTracker::clear();
}

View File

@@ -0,0 +1,65 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
#ifndef __RFB_COMPARINGUPDATETRACKER_H__
#define __RFB_COMPARINGUPDATETRACKER_H__
#include <rfb/UpdateTracker.h>
class scrollHasher_t;
namespace rfb {
class ComparingUpdateTracker : public SimpleUpdateTracker {
public:
ComparingUpdateTracker(PixelBuffer* buffer);
~ComparingUpdateTracker();
// compare() does the comparison and reduces its changed and copied regions
// as appropriate. Returns true if the regions were altered.
virtual bool compare(bool skipScrollDetection, const Region &skipCursorArea);
// enable()/disable() turns the comparing functionality on/off. With it
// disabled, the object will behave like a dumb update tracker (i.e.
// compare() will be a no-op). It is harmless to repeatedly call these
// methods.
virtual void enable();
virtual void disable();
void logStats();
virtual void getUpdateInfo(UpdateInfo* info, const Region& cliprgn);
virtual void clear();
private:
void compareRect(const Rect& r, Region* newchanged, const Region &skipCursorArea);
PixelBuffer* fb;
ManagedPixelBuffer oldFb;
bool firstCompare;
bool enabled;
bool detectScroll;
rdr::U32 totalPixels, missedPixels;
scrollHasher_t *scrollHasher;
std::vector<CopyPassRect> copyPassRects;
};
}
#endif

View File

@@ -0,0 +1,505 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2004-2005 Cendio AB.
* Copyright 2017 Peter Astrand <astrand@cendio.se> 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.
*/
// -=- Configuration.cxx
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <os/Mutex.h>
#include <rfb/util.h>
#include <rfb/Configuration.h>
#include <rfb/LogWriter.h>
#include <rfb/Exception.h>
#define LOCK_CONFIG os::AutoMutex a(mutex)
#include <rdr/HexOutStream.h>
#include <rdr/HexInStream.h>
using namespace rfb;
static LogWriter vlog("Config");
// -=- The Global/server/viewer Configuration objects
Configuration* Configuration::global_ = 0;
Configuration* Configuration::server_ = 0;
Configuration* Configuration::viewer_ = 0;
Configuration* Configuration::global() {
if (!global_)
global_ = new Configuration("Global");
return global_;
}
Configuration* Configuration::server() {
if (!server_)
server_ = new Configuration("Server");
return server_;
}
Configuration* Configuration::viewer() {
if (!viewer_)
viewer_ = new Configuration("Viewer");
return viewer_;
}
// -=- Configuration implementation
bool Configuration::set(const char* n, const char* v, bool immutable) {
return set(n, strlen(n), v, immutable);
}
bool Configuration::set(const char* name, int len,
const char* val, bool immutable)
{
VoidParameter* current = head;
while (current) {
if ((int)strlen(current->getName()) == len &&
strncasecmp(current->getName(), name, len) == 0)
{
bool b = current->setParam(val);
if (b && immutable)
current->setImmutable();
return b;
}
current = current->_next;
}
return _next ? _next->set(name, len, val, immutable) : false;
}
bool Configuration::set(const char* config, bool immutable) {
bool hyphen = false;
if (config[0] == '-') {
hyphen = true;
config++;
if (config[0] == '-') config++; // allow gnu-style --<option>
}
const char* equal = strchr(config, '=');
if (equal) {
return set(config, equal-config, equal+1, immutable);
} else if (hyphen) {
VoidParameter* current = head;
while (current) {
if (strcasecmp(current->getName(), config) == 0) {
bool b = current->setParam();
if (b && immutable)
current->setImmutable();
return b;
}
current = current->_next;
}
}
return _next ? _next->set(config, immutable) : false;
}
VoidParameter* Configuration::get(const char* param)
{
VoidParameter* current = head;
while (current) {
if (strcasecmp(current->getName(), param) == 0)
return current;
current = current->_next;
}
return _next ? _next->get(param) : 0;
}
void Configuration::list(int width, int nameWidth) {
VoidParameter* current = head;
fprintf(stderr, "%s Parameters:\n", name.buf);
while (current) {
char* def_str = current->getDefaultStr();
const char* desc = current->getDescription();
fprintf(stderr," %-*s -", nameWidth, current->getName());
int column = strlen(current->getName());
if (column < nameWidth) column = nameWidth;
column += 4;
while (true) {
const char* s = strchr(desc, ' ');
int wordLen;
if (s) wordLen = s-desc;
else wordLen = strlen(desc);
if (column + wordLen + 1 > width) {
fprintf(stderr,"\n%*s",nameWidth+4,"");
column = nameWidth+4;
}
fprintf(stderr," %.*s",wordLen,desc);
column += wordLen + 1;
desc += wordLen + 1;
if (!s) break;
}
if (def_str) {
if (column + (int)strlen(def_str) + 11 > width)
fprintf(stderr,"\n%*s",nameWidth+4,"");
fprintf(stderr," (default=%s)\n",def_str);
strFree(def_str);
} else {
fprintf(stderr,"\n");
}
current = current->_next;
}
if (_next)
_next->list(width, nameWidth);
}
bool Configuration::remove(const char* param) {
VoidParameter *current = head;
VoidParameter **prevnext = &head;
while (current) {
if (strcasecmp(current->getName(), param) == 0) {
*prevnext = current->_next;
return true;
}
prevnext = &current->_next;
current = current->_next;
}
return false;
}
// -=- VoidParameter
VoidParameter::VoidParameter(const char* name_, const char* desc_,
ConfigurationObject co)
: immutable(false), name(name_), description(desc_)
{
Configuration *conf = NULL;
switch (co) {
case ConfGlobal: conf = Configuration::global();
break;
case ConfServer: conf = Configuration::server();
break;
case ConfViewer: conf = Configuration::viewer();
break;
}
_next = conf->head;
conf->head = this;
mutex = new os::Mutex();
}
VoidParameter::~VoidParameter() {
delete mutex;
}
const char*
VoidParameter::getName() const {
return name;
}
const char*
VoidParameter::getDescription() const {
return description;
}
bool VoidParameter::setParam() {
return false;
}
bool VoidParameter::isBool() const {
return false;
}
void
VoidParameter::setImmutable() {
vlog.debug("set immutable %s", getName());
immutable = true;
}
// -=- AliasParameter
AliasParameter::AliasParameter(const char* name_, const char* desc_,
VoidParameter* param_, ConfigurationObject co)
: VoidParameter(name_, desc_, co), param(param_) {
}
bool
AliasParameter::setParam(const char* v) {
return param->setParam(v);
}
bool AliasParameter::setParam() {
return param->setParam();
}
char*
AliasParameter::getDefaultStr() const {
return 0;
}
char* AliasParameter::getValueStr() const {
return param->getValueStr();
}
bool AliasParameter::isBool() const {
return param->isBool();
}
void
AliasParameter::setImmutable() {
vlog.debug("set immutable %s (Alias)", getName());
param->setImmutable();
}
// -=- BoolParameter
BoolParameter::BoolParameter(const char* name_, const char* desc_, bool v,
ConfigurationObject co)
: VoidParameter(name_, desc_, co), value(v), def_value(v) {
}
bool
BoolParameter::setParam(const char* v) {
if (immutable) return true;
if (*v == 0 || strcasecmp(v, "1") == 0 || strcasecmp(v, "on") == 0
|| strcasecmp(v, "true") == 0 || strcasecmp(v, "yes") == 0)
value = 1;
else if (strcasecmp(v, "0") == 0 || strcasecmp(v, "off") == 0
|| strcasecmp(v, "false") == 0 || strcasecmp(v, "no") == 0)
value = 0;
else {
vlog.error("Bool parameter %s: invalid value '%s'", getName(), v);
return false;
}
vlog.debug("set %s(Bool) to %s(%d)", getName(), v, value);
return true;
}
bool BoolParameter::setParam() {
setParam(true);
return true;
}
void BoolParameter::setParam(bool b) {
if (immutable) return;
value = b;
vlog.debug("set %s(Bool) to %d", getName(), value);
}
char*
BoolParameter::getDefaultStr() const {
return strDup(def_value ? "1" : "0");
}
char* BoolParameter::getValueStr() const {
return strDup(value ? "1" : "0");
}
bool BoolParameter::isBool() const {
return true;
}
BoolParameter::operator bool() const {
return value;
}
// -=- PresetParameter
PresetParameter::PresetParameter(const char* name_, const char* desc_, bool v,
void (*onSet)(void),
ConfigurationObject co)
: BoolParameter(name_, desc_, v, co), onSetCB(onSet) {
}
bool PresetParameter::setParam(const char* value_) {
const bool ret = BoolParameter::setParam(value_);
if (ret && onSetCB && value)
onSetCB();
return ret;
}
bool PresetParameter::setParam() {
const bool ret = BoolParameter::setParam();
if (ret && onSetCB && value)
onSetCB();
return ret;
}
void PresetParameter::setParam(bool b) {
BoolParameter::setParam(b);
if (onSetCB && value)
onSetCB();
}
// -=- IntParameter
IntParameter::IntParameter(const char* name_, const char* desc_, int v,
int minValue_, int maxValue_, ConfigurationObject co)
: VoidParameter(name_, desc_, co), value(v), def_value(v),
minValue(minValue_), maxValue(maxValue_)
{
}
bool
IntParameter::setParam(const char* v) {
if (immutable) return true;
vlog.debug("set %s(Int) to %s", getName(), v);
int i = strtol(v, NULL, 0);
if (i < minValue || i > maxValue)
return false;
value = i;
return true;
}
bool
IntParameter::setParam(int v) {
if (immutable) return true;
vlog.debug("set %s(Int) to %d", getName(), v);
if (v < minValue || v > maxValue)
return false;
value = v;
return true;
}
char*
IntParameter::getDefaultStr() const {
char* result = new char[16];
sprintf(result, "%d", def_value);
return result;
}
char* IntParameter::getValueStr() const {
char* result = new char[16];
sprintf(result, "%d", value);
return result;
}
IntParameter::operator int() const {
return value;
}
// -=- StringParameter
StringParameter::StringParameter(const char* name_, const char* desc_,
const char* v, ConfigurationObject co)
: VoidParameter(name_, desc_, co), value(strDup(v)), def_value(v)
{
if (!v) {
fprintf(stderr,"Default value <null> for %s not allowed\n",name_);
throw rfb::Exception("Default value <null> not allowed");
}
}
StringParameter::~StringParameter() {
strFree(value);
}
void StringParameter::setDefaultStr(const char* v) {
def_value = v;
strFree(value);
value = strDup(v);
}
bool StringParameter::setParam(const char* v) {
LOCK_CONFIG;
if (immutable) return true;
if (!v)
throw rfb::Exception("setParam(<null>) not allowed");
vlog.debug("set %s(String) to %s", getName(), v);
CharArray oldValue(value);
value = strDup(v);
return value != 0;
}
char* StringParameter::getDefaultStr() const {
return strDup(def_value);
}
char* StringParameter::getValueStr() const {
LOCK_CONFIG;
return strDup(value);
}
StringParameter::operator const char *() const {
return value;
}
// -=- BinaryParameter
BinaryParameter::BinaryParameter(const char* name_, const char* desc_,
const void* v, int l, ConfigurationObject co)
: VoidParameter(name_, desc_, co), value(0), length(0), def_value((char*)v), def_length(l) {
if (l) {
value = new char[l];
length = l;
memcpy(value, v, l);
}
}
BinaryParameter::~BinaryParameter() {
if (value)
delete [] value;
}
bool BinaryParameter::setParam(const char* v) {
LOCK_CONFIG;
if (immutable) return true;
vlog.debug("set %s(Binary) to %s", getName(), v);
return rdr::HexInStream::hexStrToBin(v, &value, &length);
}
void BinaryParameter::setParam(const void* v, int len) {
LOCK_CONFIG;
if (immutable) return;
vlog.debug("set %s(Binary)", getName());
delete [] value; value = 0;
if (len) {
value = new char[len];
length = len;
memcpy(value, v, len);
}
}
char* BinaryParameter::getDefaultStr() const {
return rdr::HexOutStream::binToHexStr(def_value, def_length);
}
char* BinaryParameter::getValueStr() const {
LOCK_CONFIG;
return rdr::HexOutStream::binToHexStr(value, length);
}
void BinaryParameter::getData(void** data_, int* length_) const {
LOCK_CONFIG;
if (length_) *length_ = length;
if (data_) {
*data_ = new char[length];
memcpy(*data_, value, length);
}
}

312
common/rfb/Configuration.h Normal file
View File

@@ -0,0 +1,312 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
// -=- Configuration.h
//
// This header defines a set of classes used to represent configuration
// parameters of different types. Instances of the different parameter
// types are associated with instances of the Configuration class, and
// are each given a unique name. The Configuration class provides a
// generic API through which parameters may be located by name and their
// value set, thus removing the need to write platform-specific code.
// Simply defining a new parameter and associating it with a Configuration
// will allow it to be configured by the user.
//
// If no Configuration is specified when creating a Parameter, then the
// global Configuration will be assumed.
//
// Configurations can be "chained" into groups. Each group has a root
// Configuration, a pointer to which should be passed to the constructors
// of the other group members. set() and get() operations called on the
// root will iterate through all of the group's members.
//
// NB: On platforms that support Threading, locking is performed to protect
// complex parameter types from concurrent access (e.g. strings).
// NB: NO LOCKING is performed when linking Configurations to groups
// or when adding Parameters to Configurations.
#ifndef __RFB_CONFIGURATION_H__
#define __RFB_CONFIGURATION_H__
#include <rfb/util.h>
namespace os { class Mutex; }
namespace rfb {
class VoidParameter;
struct ParameterIterator;
enum ConfigurationObject { ConfGlobal, ConfServer, ConfViewer };
// -=- Configuration
// Class used to access parameters.
class Configuration {
public:
// - Create a new Configuration object
Configuration(const char* name_) : name(strDup(name_)), head(0), _next(0) {}
// - Return the buffer containing the Configuration's name
const char* getName() const { return name.buf; }
// - Set named parameter to value
bool set(const char* param, const char* value, bool immutable=false);
// - Set parameter to value (separated by "=")
bool set(const char* config, bool immutable=false);
// - Set named parameter to value, with name truncated at len
bool set(const char* name, int len,
const char* val, bool immutable);
// - Get named parameter
VoidParameter* get(const char* param);
// - List the parameters of this Configuration group
void list(int width=79, int nameWidth=10);
// - Remove a parameter from this Configuration group
bool remove(const char* param);
// - readFromFile
// Read configuration parameters from the specified file.
void readFromFile(const char* filename);
// - writeConfigToFile
// Write a new configuration parameters file, then mv it
// over the old file.
void writeToFile(const char* filename);
// - Get the Global Configuration object
// NB: This call does NOT lock the Configuration system.
// ALWAYS ensure that if you have ANY global Parameters,
// then they are defined as global objects, to ensure that
// global() is called when only the main thread is running.
static Configuration* global();
// Enable server/viewer specific parameters
static void enableServerParams() { global()->appendConfiguration(server()); }
static void enableViewerParams() { global()->appendConfiguration(viewer()); }
// - Container for process-wide Global parameters
static bool setParam(const char* param, const char* value, bool immutable=false) {
return global()->set(param, value, immutable);
}
static bool setParam(const char* config, bool immutable=false) {
return global()->set(config, immutable);
}
static bool setParam(const char* name, int len,
const char* val, bool immutable) {
return global()->set(name, len, val, immutable);
}
static VoidParameter* getParam(const char* param) { return global()->get(param); }
static void listParams(int width=79, int nameWidth=10) {
global()->list(width, nameWidth);
}
static bool removeParam(const char* param) {
return global()->remove(param);
}
private:
friend class VoidParameter;
friend struct ParameterIterator;
// Name for this Configuration
CharArray name;
// - Pointer to first Parameter in this group
VoidParameter* head;
// Pointer to next Configuration in this group
Configuration* _next;
// The process-wide, Global Configuration object
static Configuration* global_;
// The server only Configuration object
static Configuration* server_;
// The viewer only Configuration object
static Configuration* viewer_;
// Get server/viewer specific configuration object
static Configuration* server();
static Configuration* viewer();
// Append configuration object to this instance.
// NOTE: conf instance can be only one configuration object
void appendConfiguration(Configuration *conf) {
conf->_next = _next; _next = conf;
}
};
// -=- VoidParameter
// Configuration parameter base-class.
class VoidParameter {
public:
VoidParameter(const char* name_, const char* desc_, ConfigurationObject co=ConfGlobal);
virtual ~VoidParameter();
const char* getName() const;
const char* getDescription() const;
virtual bool setParam(const char* value) = 0;
virtual bool setParam();
virtual char* getDefaultStr() const = 0;
virtual char* getValueStr() const = 0;
virtual bool isBool() const;
virtual void setImmutable();
protected:
friend class Configuration;
friend struct ParameterIterator;
VoidParameter* _next;
bool immutable;
const char* name;
const char* description;
os::Mutex* mutex;
};
class AliasParameter : public VoidParameter {
public:
AliasParameter(const char* name_, const char* desc_,VoidParameter* param_,
ConfigurationObject co=ConfGlobal);
virtual bool setParam(const char* value);
virtual bool setParam();
virtual char* getDefaultStr() const;
virtual char* getValueStr() const;
virtual bool isBool() const;
virtual void setImmutable();
private:
VoidParameter* param;
};
class BoolParameter : public VoidParameter {
public:
BoolParameter(const char* name_, const char* desc_, bool v,
ConfigurationObject co=ConfGlobal);
virtual bool setParam(const char* value);
virtual bool setParam();
virtual void setParam(bool b);
virtual char* getDefaultStr() const;
virtual char* getValueStr() const;
virtual bool isBool() const;
operator bool() const;
protected:
bool value;
bool def_value;
};
class PresetParameter : public BoolParameter {
public:
PresetParameter(const char* name_, const char* desc_, bool v,
void (*onSet)(void),
ConfigurationObject co=ConfGlobal);
virtual bool setParam(const char* value);
virtual bool setParam();
virtual void setParam(bool b);
protected:
void (*onSetCB)(void);
};
class IntParameter : public VoidParameter {
public:
IntParameter(const char* name_, const char* desc_, int v,
int minValue=INT_MIN, int maxValue=INT_MAX,
ConfigurationObject co=ConfGlobal);
using VoidParameter::setParam;
virtual bool setParam(const char* value);
virtual bool setParam(int v);
virtual char* getDefaultStr() const;
virtual char* getValueStr() const;
operator int() const;
protected:
int value;
int def_value;
int minValue, maxValue;
};
class StringParameter : public VoidParameter {
public:
// StringParameter contains a null-terminated string, which CANNOT
// be Null, and so neither can the default value!
StringParameter(const char* name_, const char* desc_, const char* v,
ConfigurationObject co=ConfGlobal);
virtual ~StringParameter();
virtual bool setParam(const char* value);
virtual char* getDefaultStr() const;
virtual char* getValueStr() const;
void setDefaultStr(const char* v);
operator const char*() const;
// getData() returns a copy of the data - it must be delete[]d by the
// caller.
char* getData() const { return getValueStr(); }
protected:
char* value;
const char* def_value;
};
class BinaryParameter : public VoidParameter {
public:
BinaryParameter(const char* name_, const char* desc_, const void* v, int l,
ConfigurationObject co=ConfGlobal);
using VoidParameter::setParam;
virtual ~BinaryParameter();
virtual bool setParam(const char* value);
virtual void setParam(const void* v, int l);
virtual char* getDefaultStr() const;
virtual char* getValueStr() const;
// getData() will return length zero if there is no data
// NB: data may be set to zero, OR set to a zero-length buffer
void getData(void** data, int* length) const;
protected:
char* value;
int length;
char* def_value;
int def_length;
};
// -=- ParameterIterator
// Iterates over all enabled parameters (global + server/viewer).
// Current Parameter is accessed via param, the current Configuration
// via config. The next() method moves on to the next Parameter.
struct ParameterIterator {
ParameterIterator() : config(Configuration::global()), param(config->head) {}
void next() {
param = param->_next;
while (!param) {
config = config->_next;
if (!config) break;
param = config->head;
}
}
Configuration* config;
VoidParameter* param;
};
};
#endif // __RFB_CONFIGURATION_H__

484
common/rfb/Congestion.cxx Normal file
View File

@@ -0,0 +1,484 @@
/* Copyright 2009-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.
*/
/*
* This code implements congestion control in the same way as TCP in
* order to avoid excessive latency in the transport. This is needed
* because "buffer bloat" is unfortunately still a very real problem.
*
* The basic principle is TCP Congestion Control (RFC 5618), with the
* addition of using the TCP Vegas algorithm. The reason we use Vegas
* is that we run on top of a reliable transport so we need a latency
* based algorithm rather than a loss based one. There is also a lot of
* interpolation of values. This is because we have rather horrible
* granularity in our measurements.
*
* We use a simplistic form of slow start in order to ramp up quickly
* from an idle state. We do not have any persistent threshold though
* as we have too much noise for it to be reliable.
*/
#include <assert.h>
#include <sys/time.h>
#ifdef __linux__
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <linux/sockios.h>
#endif
#include <rfb/Congestion.h>
#include <rfb/LogWriter.h>
#include <rfb/util.h>
// Debug output on what the congestion control is up to
#undef CONGESTION_DEBUG
// Dump socket congestion window debug trace to disk
#undef CONGESTION_TRACE
using namespace rfb;
// This window should get us going fairly fast on a decent bandwidth network.
// If it's too high, it will rapidly be reduced and stay low.
static const unsigned INITIAL_WINDOW = 16384;
// TCP's minimal window is 3*MSS. But since we don't know the MSS, we
// make a guess at 4 KiB (it's probably a bit higher).
static const unsigned MINIMUM_WINDOW = 4096;
// The current default maximum window for Linux (4 MiB). Should be a good
// limit for now...
static const unsigned MAXIMUM_WINDOW = 4194304;
// Compare position even when wrapped around
static inline bool isAfter(unsigned a, unsigned b) {
return a != b && a - b <= UINT_MAX / 2;
}
static LogWriter vlog("Congestion");
Congestion::Congestion() :
lastPosition(0), extraBuffer(0),
baseRTT(-1), congWindow(INITIAL_WINDOW), inSlowStart(true),
safeBaseRTT(-1), measurements(0), minRTT(-1), minCongestedRTT(-1)
{
gettimeofday(&lastUpdate, NULL);
gettimeofday(&lastSent, NULL);
memset(&lastPong, 0, sizeof(lastPong));
gettimeofday(&lastPongArrival, NULL);
gettimeofday(&lastAdjustment, NULL);
}
Congestion::~Congestion()
{
}
void Congestion::updatePosition(unsigned pos)
{
struct timeval now;
unsigned delta, consumed;
gettimeofday(&now, NULL);
delta = pos - lastPosition;
if ((delta > 0) || (extraBuffer > 0))
lastSent = now;
// Idle for too long?
// We use a very crude RTO calculation in order to keep things simple
// FIXME: should implement RFC 2861
if (msBetween(&lastSent, &now) > __rfbmax(baseRTT*2, 100)) {
#ifdef CONGESTION_DEBUG
vlog.debug("Connection idle for %d ms, resetting congestion control",
msBetween(&lastSent, &now));
#endif
// Close congestion window and redo wire latency measurement
congWindow = __rfbmin(INITIAL_WINDOW, congWindow);
baseRTT = -1;
measurements = 0;
gettimeofday(&lastAdjustment, NULL);
minRTT = minCongestedRTT = -1;
inSlowStart = true;
}
// Commonly we will be in a state of overbuffering. We need to
// estimate the extra delay that causes so we can separate it from
// the delay caused by an incorrect congestion window.
// (we cannot do this until we have a RTT measurement though)
if (baseRTT != (unsigned)-1) {
extraBuffer += delta;
consumed = msBetween(&lastUpdate, &now) * congWindow / baseRTT;
if (extraBuffer < consumed)
extraBuffer = 0;
else
extraBuffer -= consumed;
}
lastPosition = pos;
lastUpdate = now;
}
void Congestion::sentPing()
{
struct RTTInfo rttInfo;
memset(&rttInfo, 0, sizeof(struct RTTInfo));
gettimeofday(&rttInfo.tv, NULL);
rttInfo.pos = lastPosition;
rttInfo.extra = getExtraBuffer();
rttInfo.congested = isCongested();
pings.push_back(rttInfo);
}
void Congestion::gotPong()
{
struct timeval now;
struct RTTInfo rttInfo;
unsigned rtt, delay;
if (pings.empty())
return;
gettimeofday(&now, NULL);
rttInfo = pings.front();
pings.pop_front();
lastPong = rttInfo;
lastPongArrival = now;
rtt = msBetween(&rttInfo.tv, &now);
if (rtt < 1)
rtt = 1;
// Try to estimate wire latency by tracking lowest seen latency
if (rtt < baseRTT)
safeBaseRTT = baseRTT = rtt;
// Pings sent before the last adjustment aren't interesting as they
// aren't a measurement of the current congestion window
if (isBefore(&rttInfo.tv, &lastAdjustment))
return;
// Estimate added delay because of overtaxed buffers (see above)
delay = rttInfo.extra * baseRTT / congWindow;
if (delay < rtt)
rtt -= delay;
else
rtt = 1;
// A latency less than the wire latency means that we've
// understimated the congestion window. We can't really determine
// how much, so pretend that we got no buffer latency at all.
if (rtt < baseRTT)
rtt = baseRTT;
// Record the minimum seen delay (hopefully ignores jitter) and let
// the congestion control do its thing.
//
// Note: We are delay based rather than loss based, which means we
// need to look at pongs even if they weren't limited by the
// current window ("congested"). Otherwise we will fail to
// detect increasing congestion until the application exceeds
// the congestion window.
if (rtt < minRTT)
minRTT = rtt;
if (rttInfo.congested) {
if (rtt < minCongestedRTT)
minCongestedRTT = rtt;
}
measurements++;
updateCongestion();
}
bool Congestion::isCongested()
{
if (getInFlight() < congWindow)
return false;
return true;
}
int Congestion::getUncongestedETA()
{
unsigned targetAcked;
const struct RTTInfo* prevPing;
unsigned eta, elapsed;
unsigned etaNext, delay;
std::list<struct RTTInfo>::const_iterator iter;
targetAcked = lastPosition - congWindow;
// Simple case?
if (isAfter(lastPong.pos, targetAcked))
return 0;
// No measurements yet?
if (baseRTT == (unsigned)-1)
return -1;
prevPing = &lastPong;
eta = 0;
elapsed = msSince(&lastPongArrival);
// Walk the ping queue and figure out which one we are waiting for to
// get to an uncongested state
for (iter = pings.begin(); ;++iter) {
struct RTTInfo curPing;
// If we aren't waiting for a pong that will clear the congested
// state then we have to estimate the final bit by pretending that
// we had a ping just after the last position update.
if (iter == pings.end()) {
curPing.tv = lastUpdate;
curPing.pos = lastPosition;
curPing.extra = extraBuffer;
} else {
curPing = *iter;
}
etaNext = msBetween(&prevPing->tv, &curPing.tv);
// Compensate for buffering delays
delay = curPing.extra * baseRTT / congWindow;
etaNext += delay;
delay = prevPing->extra * baseRTT / congWindow;
if (delay >= etaNext)
etaNext = 0;
else
etaNext -= delay;
// Found it?
if (isAfter(curPing.pos, targetAcked)) {
eta += etaNext * (curPing.pos - targetAcked) / (curPing.pos - prevPing->pos);
if (elapsed > eta)
return 0;
else
return eta - elapsed;
}
assert(iter != pings.end());
eta += etaNext;
prevPing = &*iter;
}
}
size_t Congestion::getBandwidth()
{
// No measurements yet? Guess RTT of 60 ms
if (safeBaseRTT == (unsigned)-1)
return congWindow * 1000 / 60;
return congWindow * 1000 / safeBaseRTT;
}
void Congestion::debugTrace(const char* filename, int fd)
{
#ifdef CONGESTION_TRACE
#ifdef __linux__
FILE *f;
f = fopen(filename, "ab");
if (f != NULL) {
struct tcp_info info;
int buffered;
socklen_t len;
len = sizeof(info);
if ((getsockopt(fd, IPPROTO_TCP,
TCP_INFO, &info, &len) == 0) &&
(ioctl(fd, SIOCOUTQ, &buffered) == 0)) {
struct timeval now;
gettimeofday(&now, NULL);
fprintf(f, "%u.%06u,%u,%u,%u,%u\n",
(unsigned)now.tv_sec, (unsigned)now.tv_usec,
congWindow, info.tcpi_snd_cwnd * info.tcpi_snd_mss,
getInFlight(), buffered);
}
fclose(f);
}
#endif
#endif
}
unsigned Congestion::getExtraBuffer()
{
unsigned elapsed;
unsigned consumed;
if (baseRTT == (unsigned)-1)
return 0;
elapsed = msSince(&lastUpdate);
consumed = elapsed * congWindow / baseRTT;
if (consumed >= extraBuffer)
return 0;
else
return extraBuffer - consumed;
}
unsigned Congestion::getInFlight()
{
struct RTTInfo nextPong;
unsigned etaNext, delay, elapsed, acked;
// Simple case?
if (lastPosition == lastPong.pos)
return 0;
// No measurements yet?
if (baseRTT == (unsigned)-1) {
if (!pings.empty())
return lastPosition - pings.front().pos;
return 0;
}
// If we aren't waiting for any pong then we have to estimate things
// by pretending that we had a ping just after the last position
// update.
if (pings.empty()) {
nextPong.tv = lastUpdate;
nextPong.pos = lastPosition;
nextPong.extra = extraBuffer;
} else {
nextPong = pings.front();
}
// First we need to estimate how many bytes have made it through
// completely. Look at the next ping that should arrive and figure
// out how far behind it should be and interpolate the positions.
etaNext = msBetween(&lastPong.tv, &nextPong.tv);
// Compensate for buffering delays
delay = nextPong.extra * baseRTT / congWindow;
etaNext += delay;
delay = lastPong.extra * baseRTT / congWindow;
if (delay >= etaNext)
etaNext = 0;
else
etaNext -= delay;
elapsed = msSince(&lastPongArrival);
// The pong should be here any second. Be optimistic and assume
// we can already use its value.
if (etaNext <= elapsed)
acked = nextPong.pos;
else {
acked = lastPong.pos;
acked += (nextPong.pos - lastPong.pos) * elapsed / etaNext;
}
return lastPosition - acked;
}
void Congestion::updateCongestion()
{
unsigned diff;
// We want at least three measurements to avoid noise
if (measurements < 3)
return;
assert(minRTT >= baseRTT);
assert(minCongestedRTT >= baseRTT);
// The goal is to have a slightly too large congestion window since
// a "perfect" one cannot be distinguished from a too small one. This
// translates to a goal of a few extra milliseconds of delay.
diff = minRTT - baseRTT;
if (diff > __rfbmax(100, baseRTT/2)) {
// We have no way of detecting loss, so assume massive latency
// spike means packet loss. Adjust the window and go directly
// to congestion avoidance.
#ifdef CONGESTION_DEBUG
vlog.debug("Latency spike! Backing off...");
#endif
congWindow = congWindow * baseRTT / minRTT;
inSlowStart = false;
}
if (inSlowStart) {
// Slow start. Aggressive growth until we see congestion.
if (diff > 25) {
// If we see an increased latency then we assume we've hit the
// limit and it's time to leave slow start and switch to
// congestion avoidance
congWindow = congWindow * baseRTT / minRTT;
inSlowStart = false;
} else {
// It's not safe to increase unless we actually used the entire
// congestion window, hence we look at minCongestedRTT and not
// minRTT
diff = minCongestedRTT - baseRTT;
if (diff < 25)
congWindow *= 2;
}
} else {
// Congestion avoidance (VEGAS)
if (diff > 50) {
// Slightly too fast
congWindow -= 4096;
} else {
// Only the "congested" pongs are checked to see if the
// window is too small.
diff = minCongestedRTT - baseRTT;
if (diff < 5) {
// Way too slow
congWindow += 8192;
} else if (diff < 25) {
// Too slow
congWindow += 4096;
}
}
}
if (congWindow < MINIMUM_WINDOW)
congWindow = MINIMUM_WINDOW;
if (congWindow > MAXIMUM_WINDOW)
congWindow = MAXIMUM_WINDOW;
#ifdef CONGESTION_DEBUG
vlog.debug("RTT: %d/%d ms (%d ms), Window: %d KiB, Bandwidth: %g Mbps%s",
minRTT, minCongestedRTT, baseRTT, congWindow / 1024,
congWindow * 8.0 / baseRTT / 1000.0,
inSlowStart ? " (slow start)" : "");
#endif
measurements = 0;
gettimeofday(&lastAdjustment, NULL);
minRTT = minCongestedRTT = -1;
}

95
common/rfb/Congestion.h Normal file
View File

@@ -0,0 +1,95 @@
/* Copyright 2009-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.
*/
#ifndef __RFB_CONGESTION_H__
#define __RFB_CONGESTION_H__
#include <list>
namespace rfb {
class Congestion {
public:
Congestion();
~Congestion();
// updatePosition() registers the current stream position and can
// and should be called often.
void updatePosition(unsigned pos);
// sentPing() must be called when a marker is placed on the
// outgoing stream. gotPong() must be called when the response for
// such a marker is received.
void sentPing();
void gotPong();
// isCongested() determines if the transport is currently congested
// or if more data can be sent.
bool isCongested();
// getUncongestedETA() returns the number of milliseconds until the
// transport is no longer congested. Returns 0 if there is no
// congestion, and -1 if it is unknown when the transport will no
// longer be congested.
int getUncongestedETA();
// getBandwidth() returns the current bandwidth estimation in bytes
// per second.
size_t getBandwidth();
// debugTrace() writes the current congestion window, as well as the
// congestion window of the underlying TCP layer, to the specified
// file
void debugTrace(const char* filename, int fd);
protected:
unsigned getExtraBuffer();
unsigned getInFlight();
void updateCongestion();
private:
unsigned lastPosition;
unsigned extraBuffer;
struct timeval lastUpdate;
struct timeval lastSent;
unsigned baseRTT;
unsigned congWindow;
bool inSlowStart;
unsigned safeBaseRTT;
struct RTTInfo {
struct timeval tv;
unsigned pos;
unsigned extra;
bool congested;
};
std::list<struct RTTInfo> pings;
struct RTTInfo lastPong;
struct timeval lastPongArrival;
int measurements;
struct timeval lastAdjustment;
unsigned minRTT, minCongestedRTT;
};
}
#endif

258
common/rfb/ConnParams.cxx Normal file
View File

@@ -0,0 +1,258 @@
/* 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 <string.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/ServerCore.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),
supportsWEBP(false),
supportsSetDesktopSize(false), supportsFence(false),
supportsContinuousUpdates(false),
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
subsampling(subsampleUndefined), name_(0), verStrPos(0),
ledState_(ledUnknown)
{
memset(kasmPassed, 0, KASM_NUM_SETTINGS);
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;
supportsWEBP = 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 pseudoEncodingWEBP:
supportsWEBP = 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;
case pseudoEncodingPreferBandwidth:
if (!rfb::Server::ignoreClientSettingsKasm)
Server::preferBandwidth.setParam();
break;
case pseudoEncodingMaxVideoResolution:
if (!rfb::Server::ignoreClientSettingsKasm)
kasmPassed[KASM_MAX_VIDEO_RESOLUTION] = true;
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 (!rfb::Server::ignoreClientSettingsKasm) {
if (encodings[i] >= pseudoEncodingJpegVideoQualityLevel0 &&
encodings[i] <= pseudoEncodingJpegVideoQualityLevel9)
Server::jpegVideoQuality.setParam(encodings[i] - pseudoEncodingJpegVideoQualityLevel0);
if (encodings[i] >= pseudoEncodingWebpVideoQualityLevel0 &&
encodings[i] <= pseudoEncodingWebpVideoQualityLevel9)
Server::webpVideoQuality.setParam(encodings[i] - pseudoEncodingWebpVideoQualityLevel0);
if (encodings[i] >= pseudoEncodingTreatLosslessLevel0 &&
encodings[i] <= pseudoEncodingTreatLosslessLevel10)
Server::treatLossless.setParam(encodings[i] - pseudoEncodingTreatLosslessLevel0);
if (encodings[i] >= pseudoEncodingDynamicQualityMinLevel0 &&
encodings[i] <= pseudoEncodingDynamicQualityMinLevel9)
Server::dynamicQualityMin.setParam(encodings[i] - pseudoEncodingDynamicQualityMinLevel0);
if (encodings[i] >= pseudoEncodingDynamicQualityMaxLevel0 &&
encodings[i] <= pseudoEncodingDynamicQualityMaxLevel9)
Server::dynamicQualityMax.setParam(encodings[i] - pseudoEncodingDynamicQualityMaxLevel0);
if (encodings[i] >= pseudoEncodingVideoAreaLevel1 &&
encodings[i] <= pseudoEncodingVideoAreaLevel100)
Server::videoArea.setParam(encodings[i] - pseudoEncodingVideoAreaLevel1 + 1);
if (encodings[i] >= pseudoEncodingVideoTimeLevel0 &&
encodings[i] <= pseudoEncodingVideoTimeLevel100)
Server::videoTime.setParam(encodings[i] - pseudoEncodingVideoTimeLevel0);
if (encodings[i] >= pseudoEncodingVideoOutTimeLevel1 &&
encodings[i] <= pseudoEncodingVideoOutTimeLevel100)
Server::videoOutTime.setParam(encodings[i] - pseudoEncodingVideoOutTimeLevel1 + 1);
if (encodings[i] >= pseudoEncodingFrameRateLevel10 &&
encodings[i] <= pseudoEncodingFrameRateLevel60)
Server::frameRate.setParam(encodings[i] - pseudoEncodingFrameRateLevel10 + 10);
if (encodings[i] >= pseudoEncodingVideoScalingLevel0 &&
encodings[i] <= pseudoEncodingVideoScalingLevel9)
Server::videoScaling.setParam(encodings[i] - pseudoEncodingVideoScalingLevel0);
}
if (encodings[i] > 0)
encodings_.insert(encodings[i]);
}
}
void ConnParams::setLEDState(unsigned int state)
{
ledState_ = state;
}

141
common/rfb/ConnParams.h Normal file
View File

@@ -0,0 +1,141 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. 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.
*/
//
// ConnParams - structure containing the connection parameters.
//
#ifndef __RFB_CONNPARAMS_H__
#define __RFB_CONNPARAMS_H__
#include <set>
#include <rdr/types.h>
#include <rfb/Cursor.h>
#include <rfb/PixelFormat.h>
#include <rfb/ScreenSet.h>
namespace rdr { class InStream; }
namespace rfb {
const int subsampleUndefined = -1;
const int subsampleNone = 0;
const int subsampleGray = 1;
const int subsample2X = 2;
const int subsample4X = 3;
const int subsample8X = 4;
const int subsample16X = 5;
class ConnParams {
public:
ConnParams();
~ConnParams();
bool readVersion(rdr::InStream* is, bool* done);
void writeVersion(rdr::OutStream* os);
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);
}
int width;
int height;
ScreenSet screenLayout;
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);
bool supportsEncoding(rdr::S32 encoding) const;
void setEncodings(int nEncodings, const rdr::S32* encodings);
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 supportsWEBP;
bool supportsSetDesktopSize;
bool supportsFence;
bool supportsContinuousUpdates;
int compressLevel;
int qualityLevel;
int fineQualityLevel;
int subsampling;
// kasm exposed settings, skippable with -IgnoreClientSettingsKasm
enum {
KASM_JPEG_VIDEO_QUALITY,
KASM_WEBP_VIDEO_QUALITY,
KASM_TREAT_LOSSLESS,
KASM_PREFER_BANDWIDTH,
KASM_DYNAMIC_QUALITY_MIN,
KASM_DYNAMIC_QUALITY_MAX,
KASM_VIDEO_AREA,
KASM_VIDEO_TIME,
KASM_FRAME_RATE,
KASM_MAX_VIDEO_RESOLUTION,
KASM_NUM_SETTINGS
};
bool kasmPassed[KASM_NUM_SETTINGS];
private:
PixelFormat pf_;
char* name_;
Cursor* cursor_;
std::set<rdr::S32> encodings_;
char verStr[13];
int verStrPos;
unsigned int ledState_;
};
}
#endif

View File

@@ -0,0 +1,65 @@
/* Copyright 2014 Pierre Ossman <ossman@cendio.se> 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 <rdr/MemInStream.h>
#include <rdr/OutStream.h>
#include <rfb/PixelBuffer.h>
#include <rfb/Region.h>
#include <rfb/CopyRectDecoder.h>
using namespace rfb;
CopyRectDecoder::CopyRectDecoder() : Decoder(DecoderPlain)
{
}
CopyRectDecoder::~CopyRectDecoder()
{
}
void CopyRectDecoder::readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os)
{
os->copyBytes(is, 4);
}
void CopyRectDecoder::getAffectedRegion(const Rect& rect,
const void* buffer,
size_t buflen,
const ConnParams& cp,
Region* region)
{
rdr::MemInStream is(buffer, buflen);
int srcX = is.readU16();
int srcY = is.readU16();
Decoder::getAffectedRegion(rect, buffer, buflen, cp, 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,
ModifiablePixelBuffer* pb)
{
rdr::MemInStream is(buffer, buflen);
int srcX = is.readU16();
int srcY = is.readU16();
pb->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY));
}

View File

@@ -0,0 +1,39 @@
/* Copyright 2014 Pierre Ossman <ossman@cendio.se> 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.
*/
#ifndef __RFB_COPYRECTDECODER_H__
#define __RFB_COPYRECTDECODER_H__
#include <rfb/Decoder.h>
namespace rfb {
class CopyRectDecoder : public Decoder {
public:
CopyRectDecoder();
virtual ~CopyRectDecoder();
virtual void readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os);
virtual void getAffectedRegion(const Rect& rect, const void* buffer,
size_t buflen, const ConnParams& cp,
Region* region);
virtual void decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
ModifiablePixelBuffer* pb);
};
}
#endif

322
common/rfb/Cursor.cxx Normal file
View File

@@ -0,0 +1,322 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2014-2017 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 <assert.h>
#include <string.h>
#include <rfb/Cursor.h>
#include <rfb/LogWriter.h>
#include <rfb/Exception.h>
using namespace rfb;
static LogWriter vlog("Cursor");
Cursor::Cursor(int width, int height, const Point& hotspot,
const rdr::U8* data) :
width_(width), height_(height), hotspot_(hotspot)
{
this->data = new rdr::U8[width_*height_*4];
memcpy(this->data, data, width_*height_*4);
}
Cursor::Cursor(const Cursor& other) :
width_(other.width_), height_(other.height_),
hotspot_(other.hotspot_)
{
data = new rdr::U8[width_*height_*4];
memcpy(data, other.data, width_*height_*4);
}
Cursor::~Cursor()
{
delete [] data;
}
static unsigned short pow223[] = { 0, 30, 143, 355, 676, 1113, 1673,
2361, 3181, 4139, 5237, 6479, 7869,
9409, 11103, 12952, 14961, 17130,
19462, 21960, 24626, 27461, 30467,
33647, 37003, 40535, 44245, 48136,
52209, 56466, 60907, 65535 };
static unsigned short ipow(unsigned short val, unsigned short lut[])
{
int idx = val >> (16-5);
int a, b;
if (val < 0x8000) {
a = lut[idx];
b = lut[idx+1];
} else {
a = lut[idx-1];
b = lut[idx];
}
return (val & 0x7ff) * (b-a) / 0x7ff + a;
}
static unsigned short srgb_to_lin(unsigned char srgb)
{
return ipow((unsigned)srgb * 65535 / 255, pow223);
}
// Floyd-Steinberg dithering
static void dither(int width, int height, int* data)
{
for (int y = 0; y < height; y++) {
for (int x_ = 0; x_ < width; x_++) {
int x = (y & 1) ? (width - x_ - 1) : x_;
int error;
if (data[x] > 32767) {
error = data[x] - 65535;
data[x] = 65535;
} else {
error = data[x] - 0;
data[x] = 0;
}
if (y & 1) {
if (x > 0) {
data[x - 1] += error * 7 / 16;
}
if ((y + 1) < height) {
if (x > 0)
data[x - 1 + width] += error * 3 / 16;
data[x + width] += error * 5 / 16;
if ((x + 1) < width)
data[x + 1] += error * 1 / 16;
}
} else {
if ((x + 1) < width) {
data[x + 1] += error * 7 / 16;
}
if ((y + 1) < height) {
if ((x + 1) < width)
data[x + 1 + width] += error * 3 / 16;
data[x + width] += error * 5 / 16;
if (x > 0)
data[x - 1] += error * 1 / 16;
}
}
}
data += width;
}
}
rdr::U8* Cursor::getBitmap() const
{
// First step is converting to luminance
int luminance[width()*height()];
int *lum_ptr = luminance;
const rdr::U8 *data_ptr = data;
for (int y = 0; y < height(); y++) {
for (int x = 0; x < width(); x++) {
// Use BT.709 coefficients for grayscale
*lum_ptr = 0;
*lum_ptr += (int)srgb_to_lin(data_ptr[0]) * 6947; // 0.2126
*lum_ptr += (int)srgb_to_lin(data_ptr[1]) * 23436; // 0.7152
*lum_ptr += (int)srgb_to_lin(data_ptr[2]) * 2366; // 0.0722
*lum_ptr /= 32768;
lum_ptr++;
data_ptr += 4;
}
}
// Then diterhing
dither(width(), height(), luminance);
// Then conversion to a bit mask
rdr::U8Array source((width()+7)/8*height());
memset(source.buf, 0, (width()+7)/8*height());
int maskBytesPerRow = (width() + 7) / 8;
lum_ptr = luminance;
data_ptr = data;
for (int y = 0; y < height(); y++) {
for (int x = 0; x < width(); x++) {
int byte = y * maskBytesPerRow + x / 8;
int bit = 7 - x % 8;
if (*lum_ptr > 32767)
source.buf[byte] |= (1 << bit);
lum_ptr++;
data_ptr += 4;
}
}
return source.takeBuf();
}
rdr::U8* Cursor::getMask() const
{
// First step is converting to integer array
int alpha[width()*height()];
int *alpha_ptr = alpha;
const rdr::U8 *data_ptr = data;
for (int y = 0; y < height(); y++) {
for (int x = 0; x < width(); x++) {
*alpha_ptr = (int)data_ptr[3] * 65535 / 255;
alpha_ptr++;
data_ptr += 4;
}
}
// Then diterhing
dither(width(), height(), alpha);
// Then conversion to a bit mask
rdr::U8Array mask((width()+7)/8*height());
memset(mask.buf, 0, (width()+7)/8*height());
int maskBytesPerRow = (width() + 7) / 8;
alpha_ptr = alpha;
data_ptr = data;
for (int y = 0; y < height(); y++) {
for (int x = 0; x < width(); x++) {
int byte = y * maskBytesPerRow + x / 8;
int bit = 7 - x % 8;
if (*alpha_ptr > 32767)
mask.buf[byte] |= (1 << bit);
alpha_ptr++;
data_ptr += 4;
}
}
return mask.takeBuf();
}
// crop() determines the "busy" rectangle for the cursor - the minimum bounding
// rectangle containing actual pixels. This isn't the most efficient algorithm
// but it's short. For sanity, we make sure that the busy rectangle always
// includes the hotspot (the hotspot is unsigned on the wire so otherwise it
// would cause problems if it was above or left of the actual pixels)
void Cursor::crop()
{
Rect busy = Rect(0, 0, width_, height_);
busy = busy.intersect(Rect(hotspot_.x, hotspot_.y,
hotspot_.x+1, hotspot_.y+1));
int x, y;
rdr::U8 *data_ptr = data;
for (y = 0; y < height(); y++) {
for (x = 0; x < width(); x++) {
if (data_ptr[3] > 0) {
if (x < busy.tl.x) busy.tl.x = x;
if (x+1 > busy.br.x) busy.br.x = x+1;
if (y < busy.tl.y) busy.tl.y = y;
if (y+1 > busy.br.y) busy.br.y = y+1;
}
data_ptr += 4;
}
}
if (width() == busy.width() && height() == busy.height()) return;
// Copy the pixel data
int newDataLen = busy.area() * 4;
rdr::U8* newData = new rdr::U8[newDataLen];
data_ptr = newData;
for (y = busy.tl.y; y < busy.br.y; y++) {
memcpy(data_ptr, data + y*width()*4 + busy.tl.x*4, busy.width()*4);
data_ptr += busy.width()*4;
}
// Set the size and data to the new, cropped cursor.
width_ = busy.width();
height_ = busy.height();
hotspot_ = hotspot_.subtract(busy.tl);
delete [] data;
data = newData;
}
RenderedCursor::RenderedCursor()
{
}
const rdr::U8* RenderedCursor::getBuffer(const Rect& _r, int* stride) const
{
Rect r;
r = _r.translate(offset.negate());
if (!r.enclosed_by(buffer.getRect()))
throw Exception("RenderedCursor: Invalid area requested");
return buffer.getBuffer(r, stride);
}
void RenderedCursor::update(PixelBuffer* framebuffer,
Cursor* cursor, const Point& pos)
{
Point rawOffset, diff;
Rect clippedRect;
const rdr::U8* data;
int stride;
assert(framebuffer);
assert(cursor);
format = framebuffer->getPF();
width_ = framebuffer->width();
height_ = framebuffer->height();
rawOffset = pos.subtract(cursor->hotspot());
clippedRect = Rect(0, 0, cursor->width(), cursor->height())
.translate(rawOffset)
.intersect(framebuffer->getRect());
offset = clippedRect.tl;
buffer.setPF(format);
buffer.setSize(clippedRect.width(), clippedRect.height());
// Bail out early to avoid pestering the framebuffer with
// bogus coordinates
if (clippedRect.area() == 0)
return;
data = framebuffer->getBuffer(buffer.getRect(offset), &stride);
buffer.imageRect(buffer.getRect(), data, stride);
diff = offset.subtract(rawOffset);
for (int y = 0;y < buffer.height();y++) {
for (int x = 0;x < buffer.width();x++) {
size_t idx;
rdr::U8 bg[4], fg[4];
rdr::U8 rgb[3];
idx = (y+diff.y)*cursor->width() + (x+diff.x);
memcpy(fg, cursor->getBuffer() + idx*4, 4);
if (fg[3] == 0x00)
continue;
else if (fg[3] == 0xff) {
memcpy(rgb, fg, 3);
} else {
buffer.getImage(bg, Rect(x, y, x+1, y+1));
format.rgbFromBuffer(rgb, bg, 1);
// FIXME: Gamma aware blending
for (int i = 0;i < 3;i++) {
rgb[i] = (unsigned)rgb[i]*(255-fg[3])/255 +
(unsigned)fg[i]*fg[3]/255;
}
}
format.bufferFromRGB(bg, rgb, 1);
buffer.imageRect(Rect(x, y, x+1, y+1), bg);
}
}
}

73
common/rfb/Cursor.h Normal file
View File

@@ -0,0 +1,73 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2014-2017 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.
*/
//
// Cursor - structure containing information describing
// the current cursor shape
//
#ifndef __RFB_CURSOR_H__
#define __RFB_CURSOR_H__
#include <rfb/PixelBuffer.h>
namespace rfb {
class Cursor {
public:
Cursor(int width, int height, const Point& hotspot, const rdr::U8* data);
Cursor(const Cursor& other);
~Cursor();
int width() const { return width_; };
int height() const { return height_; };
const Point& hotspot() const { return hotspot_; };
const rdr::U8* getBuffer() const { return data; };
// getBitmap() returns a monochrome version of the cursor
rdr::U8* getBitmap() const;
// getMask() returns a simple mask version of the alpha channel
rdr::U8* getMask() const;
// crop() crops the cursor down to the smallest possible size, based on the
// mask.
void crop();
protected:
int width_, height_;
Point hotspot_;
rdr::U8* data;
};
class RenderedCursor : public PixelBuffer {
public:
RenderedCursor();
Rect getEffectiveRect() const { return buffer.getRect(offset); }
virtual const rdr::U8* getBuffer(const Rect& r, int* stride) const;
void update(PixelBuffer* framebuffer, Cursor* cursor, const Point& pos);
protected:
ManagedPixelBuffer buffer;
Point offset;
};
}
#endif

View File

@@ -0,0 +1,365 @@
/* Copyright 2015 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 <assert.h>
#include <string.h>
#include <rfb/CConnection.h>
#include <rfb/DecodeManager.h>
#include <rfb/Decoder.h>
#include <rfb/Region.h>
#include <rfb/LogWriter.h>
#include <rdr/Exception.h>
#include <rdr/MemOutStream.h>
#include <os/Mutex.h>
using namespace rfb;
static LogWriter vlog("DecodeManager");
DecodeManager::DecodeManager(CConnection *conn) :
conn(conn), threadException(NULL)
{
size_t cpuCount;
memset(decoders, 0, sizeof(decoders));
queueMutex = new os::Mutex();
producerCond = new os::Condition(queueMutex);
consumerCond = new os::Condition(queueMutex);
cpuCount = os::Thread::getSystemCPUCount();
if (cpuCount == 0) {
vlog.error("Unable to determine the number of CPU cores on this system");
cpuCount = 1;
} else {
vlog.info("Detected %d CPU core(s)", (int)cpuCount);
// No point creating more threads than this, they'll just end up
// wasting CPU fighting for locks
if (cpuCount > 4)
cpuCount = 4;
// The overhead of threading is small, but not small enough to
// ignore on single CPU systems
if (cpuCount == 1)
vlog.info("Decoding data on main thread");
else
vlog.info("Creating %d decoder thread(s)", (int)cpuCount);
}
if (cpuCount == 1) {
// Threads are not used on single CPU machines
freeBuffers.push_back(new rdr::MemOutStream());
return;
}
while (cpuCount--) {
// Twice as many possible entries in the queue as there
// are worker threads to make sure they don't stall
freeBuffers.push_back(new rdr::MemOutStream());
freeBuffers.push_back(new rdr::MemOutStream());
threads.push_back(new DecodeThread(this));
}
}
DecodeManager::~DecodeManager()
{
while (!threads.empty()) {
delete threads.back();
threads.pop_back();
}
delete threadException;
while (!freeBuffers.empty()) {
delete freeBuffers.back();
freeBuffers.pop_back();
}
delete consumerCond;
delete producerCond;
delete queueMutex;
for (size_t i = 0; i < sizeof(decoders)/sizeof(decoders[0]); i++)
delete decoders[i];
}
void DecodeManager::decodeRect(const Rect& r, int encoding,
ModifiablePixelBuffer* pb)
{
Decoder *decoder;
rdr::MemOutStream *bufferStream;
QueueEntry *entry;
assert(pb != NULL);
if (!Decoder::supported(encoding)) {
vlog.error("Unknown encoding %d", encoding);
throw rdr::Exception("Unknown encoding");
}
if (!decoders[encoding]) {
decoders[encoding] = Decoder::createDecoder(encoding);
if (!decoders[encoding]) {
vlog.error("Unknown encoding %d", encoding);
throw rdr::Exception("Unknown encoding");
}
}
decoder = decoders[encoding];
// Fast path for single CPU machines to avoid the context
// switching overhead
if (threads.empty()) {
bufferStream = freeBuffers.front();
bufferStream->clear();
decoder->readRect(r, conn->getInStream(), conn->cp, bufferStream);
decoder->decodeRect(r, bufferStream->data(), bufferStream->length(),
conn->cp, pb);
return;
}
// Wait for an available memory buffer
queueMutex->lock();
while (freeBuffers.empty())
producerCond->wait();
// Don't pop the buffer in case we throw an exception
// whilst reading
bufferStream = freeBuffers.front();
queueMutex->unlock();
// First check if any thread has encountered a problem
throwThreadException();
// Read the rect
bufferStream->clear();
decoder->readRect(r, conn->getInStream(), conn->cp, bufferStream);
// Then try to put it on the queue
entry = new QueueEntry;
entry->active = false;
entry->rect = r;
entry->encoding = encoding;
entry->decoder = decoder;
entry->cp = &conn->cp;
entry->pb = pb;
entry->bufferStream = bufferStream;
decoder->getAffectedRegion(r, bufferStream->data(),
bufferStream->length(), conn->cp,
&entry->affectedRegion);
queueMutex->lock();
// The workers add buffers to the end so it's safe to assume
// the front is still the same buffer
freeBuffers.pop_front();
workQueue.push_back(entry);
// We only put a single entry on the queue so waking a single
// thread is sufficient
consumerCond->signal();
queueMutex->unlock();
}
void DecodeManager::flush()
{
queueMutex->lock();
while (!workQueue.empty())
producerCond->wait();
queueMutex->unlock();
throwThreadException();
}
void DecodeManager::setThreadException(const rdr::Exception& e)
{
os::AutoMutex a(queueMutex);
if (threadException != NULL)
return;
threadException = new rdr::Exception("Exception on worker thread: %s", e.str());
}
void DecodeManager::throwThreadException()
{
os::AutoMutex a(queueMutex);
if (threadException == NULL)
return;
rdr::Exception e(*threadException);
delete threadException;
threadException = NULL;
throw e;
}
DecodeManager::DecodeThread::DecodeThread(DecodeManager* manager)
{
this->manager = manager;
stopRequested = false;
start();
}
DecodeManager::DecodeThread::~DecodeThread()
{
stop();
wait();
}
void DecodeManager::DecodeThread::stop()
{
os::AutoMutex a(manager->queueMutex);
if (!isRunning())
return;
stopRequested = true;
// We can't wake just this thread, so wake everyone
manager->consumerCond->broadcast();
}
void DecodeManager::DecodeThread::worker()
{
manager->queueMutex->lock();
while (!stopRequested) {
DecodeManager::QueueEntry *entry;
// Look for an available entry in the work queue
entry = findEntry();
if (entry == NULL) {
// Wait and try again
manager->consumerCond->wait();
continue;
}
// This is ours now
entry->active = true;
manager->queueMutex->unlock();
// Do the actual decoding
try {
entry->decoder->decodeRect(entry->rect, entry->bufferStream->data(),
entry->bufferStream->length(),
*entry->cp, entry->pb);
} catch (rdr::Exception& e) {
manager->setThreadException(e);
} catch(...) {
assert(false);
}
manager->queueMutex->lock();
// Remove the entry from the queue and give back the memory buffer
manager->freeBuffers.push_back(entry->bufferStream);
manager->workQueue.remove(entry);
delete entry;
// Wake the main thread in case it is waiting for a memory buffer
manager->producerCond->signal();
// This rect might have been blocking multiple other rects, so
// wake up every worker thread
if (manager->workQueue.size() > 1)
manager->consumerCond->broadcast();
}
manager->queueMutex->unlock();
}
DecodeManager::QueueEntry* DecodeManager::DecodeThread::findEntry()
{
std::list<DecodeManager::QueueEntry*>::iterator iter;
Region lockedRegion;
if (manager->workQueue.empty())
return NULL;
if (!manager->workQueue.front()->active)
return manager->workQueue.front();
for (iter = manager->workQueue.begin();
iter != manager->workQueue.end();
++iter) {
DecodeManager::QueueEntry* entry;
std::list<DecodeManager::QueueEntry*>::iterator iter2;
entry = *iter;
// Another thread working on this?
if (entry->active)
goto next;
// If this is an ordered decoder then make sure this is the first
// rectangle in the queue for that decoder
if (entry->decoder->flags & DecoderOrdered) {
for (iter2 = manager->workQueue.begin(); iter2 != iter; ++iter2) {
if (entry->encoding == (*iter2)->encoding)
goto next;
}
}
// For a partially ordered decoder we must ask the decoder for each
// pair of rectangles.
if (entry->decoder->flags & DecoderPartiallyOrdered) {
for (iter2 = manager->workQueue.begin(); iter2 != iter; ++iter2) {
if (entry->encoding != (*iter2)->encoding)
continue;
if (entry->decoder->doRectsConflict(entry->rect,
entry->bufferStream->data(),
entry->bufferStream->length(),
(*iter2)->rect,
(*iter2)->bufferStream->data(),
(*iter2)->bufferStream->length(),
*entry->cp))
goto next;
}
}
// Check overlap with earlier rectangles
if (!lockedRegion.intersect(entry->affectedRegion).is_empty())
goto next;
return entry;
next:
lockedRegion.assign_union(entry->affectedRegion);
}
return NULL;
}

104
common/rfb/DecodeManager.h Normal file
View File

@@ -0,0 +1,104 @@
/* Copyright 2015 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.
*/
#ifndef __RFB_DECODEMANAGER_H__
#define __RFB_DECODEMANAGER_H__
#include <list>
#include <os/Thread.h>
#include <rfb/Region.h>
#include <rfb/encodings.h>
namespace os {
class Condition;
class Mutex;
}
namespace rdr {
struct Exception;
class MemOutStream;
}
namespace rfb {
class CConnection;
class Decoder;
class ModifiablePixelBuffer;
struct Rect;
class DecodeManager {
public:
DecodeManager(CConnection *conn);
~DecodeManager();
void decodeRect(const Rect& r, int encoding,
ModifiablePixelBuffer* pb);
void flush();
private:
void setThreadException(const rdr::Exception& e);
void throwThreadException();
private:
CConnection *conn;
Decoder *decoders[encodingMax+1];
struct QueueEntry {
bool active;
Rect rect;
int encoding;
Decoder* decoder;
const ConnParams* cp;
ModifiablePixelBuffer* pb;
rdr::MemOutStream* bufferStream;
Region affectedRegion;
};
std::list<rdr::MemOutStream*> freeBuffers;
std::list<QueueEntry*> workQueue;
os::Mutex* queueMutex;
os::Condition* producerCond;
os::Condition* consumerCond;
private:
class DecodeThread : public os::Thread {
public:
DecodeThread(DecodeManager* manager);
~DecodeThread();
void stop();
protected:
void worker();
DecodeManager::QueueEntry* findEntry();
private:
DecodeManager* manager;
bool stopRequested;
};
std::list<DecodeThread*> threads;
rdr::Exception *threadException;
};
}
#endif

88
common/rfb/Decoder.cxx Normal file
View File

@@ -0,0 +1,88 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. 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 <rfb/encodings.h>
#include <rfb/Region.h>
#include <rfb/Decoder.h>
#include <rfb/RawDecoder.h>
#include <rfb/CopyRectDecoder.h>
#include <rfb/RREDecoder.h>
#include <rfb/HextileDecoder.h>
#include <rfb/ZRLEDecoder.h>
#include <rfb/TightDecoder.h>
using namespace rfb;
Decoder::Decoder(enum DecoderFlags flags) : flags(flags)
{
}
Decoder::~Decoder()
{
}
void Decoder::getAffectedRegion(const Rect& rect, const void* buffer,
size_t buflen, const ConnParams& cp,
Region* region)
{
region->reset(rect);
}
bool Decoder::doRectsConflict(const Rect& rectA, const void* bufferA,
size_t buflenA, const Rect& rectB,
const void* bufferB, size_t buflenB,
const ConnParams& cp)
{
return false;
}
bool Decoder::supported(int encoding)
{
switch (encoding) {
case encodingRaw:
case encodingCopyRect:
case encodingRRE:
case encodingHextile:
case encodingZRLE:
case encodingTight:
return true;
default:
return false;
}
}
Decoder* Decoder::createDecoder(int encoding)
{
switch (encoding) {
case encodingRaw:
return new RawDecoder();
case encodingCopyRect:
return new CopyRectDecoder();
case encodingRRE:
return new RREDecoder();
case encodingHextile:
return new HextileDecoder();
case encodingZRLE:
return new ZRLEDecoder();
case encodingTight:
return new TightDecoder();
default:
return NULL;
}
}

97
common/rfb/Decoder.h Normal file
View File

@@ -0,0 +1,97 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. 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.
*/
#ifndef __RFB_DECODER_H__
#define __RFB_DECODER_H__
namespace rdr {
class InStream;
class OutStream;
}
namespace rfb {
class ConnParams;
class ModifiablePixelBuffer;
class Region;
struct Rect;
enum DecoderFlags {
// A constant for decoders that don't need anything special
DecoderPlain = 0,
// All rects for this decoder must be handled in order
DecoderOrdered = 1 << 0,
// Only some of the rects must be handled in order,
// see doesRectsConflict()
DecoderPartiallyOrdered = 1 << 1,
};
class Decoder {
public:
Decoder(enum DecoderFlags flags);
virtual ~Decoder();
// These functions are the main interface to an individual decoder
// readRect() transfers data for the given rectangle from the
// InStream to the OutStream, possibly changing it along the way to
// 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;
// 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
// read and update internal state as necessary.
// getAffectedRegion() returns the parts of the frame buffer will
// 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,
Region* region);
// doesRectsConflict() determines if two rectangles must be decoded
// in the order they were received. This will only be called if the
// DecoderPartiallyOrdered flag has been set.
virtual bool doRectsConflict(const Rect& rectA,
const void* bufferA,
size_t buflenA,
const Rect& rectB,
const void* bufferB,
size_t buflenB,
const ConnParams& cp);
// 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,
ModifiablePixelBuffer* pb)=0;
public:
static bool supported(int encoding);
static Decoder* createDecoder(int encoding);
public:
const enum DecoderFlags flags;
};
}
#endif

72
common/rfb/EncCache.cxx Normal file
View File

@@ -0,0 +1,72 @@
/* 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 (C) 2019 Lauri Kasanen
*
* 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/EncCache.h>
using namespace rfb;
EncCache::EncCache() {
enabled = false;
}
EncCache::~EncCache() {
}
void EncCache::clear() {
std::map<EncId, const void *>::iterator it;
for (it = cache.begin(); it != cache.end(); it++)
free((void *) it->second);
cache.clear();
}
void EncCache::add(uint8_t type, uint16_t x, uint16_t y, uint16_t w, uint16_t h,
uint32_t len, const void *data) {
EncId id;
id.type = type;
id.x = x;
id.y = y;
id.w = w;
id.h = h;
id.len = len;
cache[id] = data;
}
const void *EncCache::get(uint8_t type, uint16_t x, uint16_t y, uint16_t w, uint16_t h,
uint32_t &len) const {
EncId id;
id.type = type;
id.x = x;
id.y = y;
id.w = w;
id.h = h;
std::map<EncId, const void *>::const_iterator it = cache.find(id);
if (it == cache.end())
return NULL;
len = it->first.len;
return it->second;
}

65
common/rfb/EncCache.h Normal file
View File

@@ -0,0 +1,65 @@
/* 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 (C) 2019 Lauri Kasanen
*
* 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.
*/
#ifndef __RFB_ENCCACHE_H__
#define __RFB_ENCCACHE_H__
#include <map>
#include <rdr/types.h>
#include <stdint.h>
#include <stdlib.h>
namespace rfb {
struct EncId {
uint8_t type;
uint16_t x, y, w, h;
uint32_t len;
bool operator <(const EncId &other) const {
return type < other.type ||
x < other.x ||
y < other.y ||
w < other.w ||
h < other.h;
}
};
class EncCache {
public:
EncCache();
~EncCache();
void clear();
void add(uint8_t type, uint16_t x, uint16_t y, uint16_t w, uint16_t h,
uint32_t len, const void *data);
const void *get(uint8_t type, uint16_t x, uint16_t y, uint16_t w, uint16_t h,
uint32_t &len) const;
bool enabled;
protected:
std::map<EncId, const void *> cache;
};
}
#endif

1629
common/rfb/EncodeManager.cxx Normal file

File diff suppressed because it is too large Load Diff

194
common/rfb/EncodeManager.h Normal file
View File

@@ -0,0 +1,194 @@
/* 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 (C) 2018 Lauri Kasanen
*
* 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.
*/
#ifndef __RFB_ENCODEMANAGER_H__
#define __RFB_ENCODEMANAGER_H__
#include <vector>
#include <list>
#include <rdr/types.h>
#include <rfb/PixelBuffer.h>
#include <rfb/Region.h>
#include <rfb/Timer.h>
#include <rfb/UpdateTracker.h>
#include <rfb/util.h>
#include <stdint.h>
#include <sys/time.h>
namespace rfb {
class SConnection;
class Encoder;
class UpdateInfo;
class Palette;
class PixelBuffer;
class RenderedCursor;
class EncCache;
struct Rect;
struct RectInfo;
struct QualityInfo;
class EncodeManager: public Timer::Callback {
public:
EncodeManager(SConnection* conn, EncCache *encCache);
~EncodeManager();
void logStats();
// Hack to let ConnParams calculate the client's preferred encoding
static bool supported(int encoding);
bool needsLosslessRefresh(const Region& req);
void pruneLosslessRefresh(const Region& limits);
void writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb,
const RenderedCursor* renderedCursor,
size_t maxUpdateSize = 2000);
void writeLosslessRefresh(const Region& req, const PixelBuffer* pb,
const RenderedCursor* renderedCursor,
size_t maxUpdateSize);
protected:
void doUpdate(bool allowLossy, const Region& changed,
const Region& copied, const Point& copy_delta,
const std::vector<CopyPassRect> &copypassed,
const PixelBuffer* pb,
const RenderedCursor* renderedCursor);
void prepareEncoders(bool allowLossy);
Region getLosslessRefresh(const Region& req, size_t maxUpdateSize);
int computeNumRects(const Region& changed);
Encoder *startRect(const Rect& rect, int type, const bool trackQuality = true,
const uint8_t isWebp = 0);
void endRect(const uint8_t isWebp = 0);
void writeCopyRects(const Region& copied, const Point& delta);
void writeCopyPassRects(const std::vector<CopyPassRect>& copypassed);
void writeSolidRects(Region *changed, const PixelBuffer* pb);
void findSolidRect(const Rect& rect, Region *changed, const PixelBuffer* pb);
void writeRects(const Region& changed, const PixelBuffer* pb,
const struct timeval *start = NULL,
const bool mainScreen = false);
void checkWebpFallback(const struct timeval *start);
void updateVideoStats(const std::vector<Rect> &rects, const PixelBuffer* pb);
void writeSubRect(const Rect& rect, const PixelBuffer *pb, const uint8_t type,
const Palette& pal, const std::vector<uint8_t> &compressed,
const uint8_t isWebp);
uint8_t getEncoderType(const Rect& rect, const PixelBuffer *pb, Palette *pal,
std::vector<uint8_t> &compressed, uint8_t *isWebp,
uint8_t *fromCache,
const PixelBuffer *scaledpb, const Rect& scaledrect) const;
virtual bool handleTimeout(Timer* t);
bool checkSolidTile(const Rect& r, const rdr::U8* colourValue,
const PixelBuffer *pb);
void extendSolidAreaByBlock(const Rect& r, const rdr::U8* colourValue,
const PixelBuffer *pb, Rect* er);
void extendSolidAreaByPixel(const Rect& r, const Rect& sr,
const rdr::U8* colourValue,
const PixelBuffer *pb, Rect* er);
PixelBuffer* preparePixelBuffer(const Rect& rect,
const PixelBuffer *pb, bool convert) const;
bool analyseRect(const PixelBuffer *pb,
struct RectInfo *info, int maxColours) const;
void updateQualities();
void trackRectQuality(const Rect& rect);
unsigned getQuality(const Rect& rect) const;
unsigned scaledQuality(const Rect& rect) const;
protected:
// Preprocessor generated, optimised methods
inline bool checkSolidTile(const Rect& r, rdr::U8 colourValue,
const PixelBuffer *pb);
inline bool checkSolidTile(const Rect& r, rdr::U16 colourValue,
const PixelBuffer *pb);
inline bool checkSolidTile(const Rect& r, rdr::U32 colourValue,
const PixelBuffer *pb);
inline bool analyseRect(int width, int height,
const rdr::U8* buffer, int stride,
struct RectInfo *info, int maxColours) const;
inline bool analyseRect(int width, int height,
const rdr::U16* buffer, int stride,
struct RectInfo *info, int maxColours) const;
inline bool analyseRect(int width, int height,
const rdr::U32* buffer, int stride,
struct RectInfo *info, int maxColours) const;
protected:
SConnection *conn;
std::vector<Encoder*> encoders;
std::vector<int> activeEncoders;
Region lossyRegion;
struct EncoderStats {
unsigned rects;
unsigned long long bytes;
unsigned long long pixels;
unsigned long long equivalent;
};
typedef std::vector< std::vector<struct EncoderStats> > StatsVector;
std::list<QualityInfo*> qualityList;
int dynamicQualityMin;
int dynamicQualityOff;
unsigned char *areaPercentages;
unsigned areaCur;
bool videoDetected;
Timer videoTimer;
uint16_t maxVideoX, maxVideoY;
unsigned updates;
EncoderStats copyStats;
StatsVector stats;
int activeType;
int beforeLength;
size_t curMaxUpdateSize;
unsigned webpFallbackUs;
unsigned webpBenchResult;
bool webpTookTooLong;
EncCache *encCache;
class OffsetPixelBuffer : public FullFramePixelBuffer {
public:
OffsetPixelBuffer() {}
virtual ~OffsetPixelBuffer() {}
void update(const PixelFormat& pf, int width, int height,
const rdr::U8* data_, int stride);
};
};
}
#endif

View File

@@ -0,0 +1,98 @@
/* Copyright (C) 2000-2003 Constantin Kaplinsky. 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.
*/
#define CONCAT2(a,b) a##b
#define CONCAT2E(a,b) CONCAT2(a,b)
#define UBPP CONCAT2E(U,BPP)
inline bool EncodeManager::checkSolidTile(const Rect& r,
rdr::UBPP colourValue,
const PixelBuffer *pb)
{
int w, h;
const rdr::UBPP* buffer;
int stride, pad;
w = r.width();
h = r.height();
buffer = (const rdr::UBPP*)pb->getBuffer(r, &stride);
pad = stride - w;
while (h--) {
int w_ = w;
while (w_--) {
if (*buffer != colourValue)
return false;
buffer++;
}
buffer += pad;
}
return true;
}
inline bool EncodeManager::analyseRect(int width, int height,
const rdr::UBPP* buffer, int stride,
struct RectInfo *info, int maxColours) const
{
int pad;
rdr::UBPP colour;
int count;
info->rleRuns = 0;
info->palette->clear();
pad = stride - width;
// For efficiency, we only update the palette on changes in colour
colour = buffer[0];
count = 0;
while (height--) {
int w_ = width;
while (w_--) {
if (*buffer != colour) {
if (!info->palette->insert(colour, count))
return false;
if (info->palette->size() > maxColours)
return false;
// FIXME: This doesn't account for switching lines
info->rleRuns++;
colour = *buffer;
count = 0;
}
buffer++;
count++;
}
buffer += pad;
}
// Make sure the final pixels also get counted
if (!info->palette->insert(colour, count))
return false;
if (info->palette->size() > maxColours)
return false;
return true;
}

81
common/rfb/Encoder.cxx Normal file
View File

@@ -0,0 +1,81 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. 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 <rfb/Encoder.h>
#include <rfb/PixelBuffer.h>
#include <rfb/Palette.h>
using namespace rfb;
Encoder::Encoder(SConnection *conn_, int encoding_,
enum EncoderFlags flags_, unsigned int maxPaletteSize_) :
encoding(encoding_), flags(flags_),
maxPaletteSize(maxPaletteSize_), conn(conn_)
{
}
Encoder::~Encoder()
{
}
void Encoder::writeSolidRect(int width, int height,
const PixelFormat& pf, const rdr::U8* colour)
{
ManagedPixelBuffer buffer(pf, width, height);
Palette palette;
rdr::U32 palcol;
buffer.fillRect(buffer.getRect(), colour);
palcol = 0;
memcpy(&palcol, colour, pf.bpp/8);
palette.insert(palcol, 1);
writeRect(&buffer, palette);
}
void Encoder::writeSolidRect(const PixelBuffer* pb, const Palette& palette)
{
rdr::U32 col32;
rdr::U16 col16;
rdr::U8 col8;
rdr::U8* buffer;
assert(palette.size() == 1);
// The Palette relies on implicit up and down conversion
switch (pb->getPF().bpp) {
case 32:
col32 = (rdr::U32)palette.getColour(0);
buffer = (rdr::U8*)&col32;
break;
case 16:
col16 = (rdr::U16)palette.getColour(0);
buffer = (rdr::U8*)&col16;
break;
default:
col8 = (rdr::U8)palette.getColour(0);
buffer = (rdr::U8*)&col8;
break;
}
writeSolidRect(pb->width(), pb->height(), pb->getPF(), buffer);
}

102
common/rfb/Encoder.h Normal file
View File

@@ -0,0 +1,102 @@
/* 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.
*/
#ifndef __RFB_ENCODER_H__
#define __RFB_ENCODER_H__
#include <rdr/types.h>
#include <rfb/Rect.h>
namespace rfb {
class SConnection;
class PixelBuffer;
class Palette;
class PixelFormat;
enum EncoderFlags {
// A constant for encoders that don't need anything special
EncoderPlain = 0,
// Give us the raw frame buffer, and not something converted to
// the what the client is asking for.
EncoderUseNativePF = 1 << 0,
// Encoder does not encode pixels perfectly accurate
EncoderLossy = 1 << 1,
};
class Encoder {
public:
Encoder(SConnection* conn, int encoding,
enum EncoderFlags flags, unsigned int maxPaletteSize);
virtual ~Encoder();
// isSupported() should return a boolean indicating if this encoder
// is okay to use with the current connection. This usually involves
// checking the list of encodings in the connection parameters.
virtual bool isSupported()=0;
virtual void setCompressLevel(int level) {};
virtual void setQualityLevel(int level) {};
virtual void setFineQualityLevel(int quality, int subsampling) {};
virtual bool treatLossless() { return !(flags & EncoderLossy); }
// writeRect() is the main interface that encodes the given rectangle
// with data from the PixelBuffer onto the SConnection given at
// encoder creation.
//
// The PixelBuffer will be in the PixelFormat specified in ConnParams
// unless the flag UseNativePF is specified. In that case the
// PixelBuffer will remain in its native format and encoder will have
// to handle any conversion itself.
//
// The Palette will always be in the PixelFormat specified in
// ConnParams. An empty palette indicates a large number of colours,
// but could still be less than maxPaletteSize.
virtual void writeRect(const PixelBuffer* pb, const Palette& palette)=0;
// writeSolidRect() is a short cut in order to encode single colour
// rectangles efficiently without having to create a fake single
// colour PixelBuffer. The colour argument follows the same semantics
// as the PixelBuffer for writeRect().
//
// Note that there is a default implementation that can be called
// using Encoder::writeSolidRect() in the event that there is no
// efficient short cut.
virtual void writeSolidRect(int width, int height,
const PixelFormat& pf,
const rdr::U8* colour)=0;
protected:
// Helper method for redirecting a single colour palette to the
// short cut method.
void writeSolidRect(const PixelBuffer* pb, const Palette& palette);
public:
const int encoding;
const enum EncoderFlags flags;
// Maximum size of the palette per rect
const unsigned int maxPaletteSize;
protected:
SConnection* conn;
};
}
#endif

42
common/rfb/Exception.h Normal file
View File

@@ -0,0 +1,42 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
#ifndef __RFB_EXCEPTION_H__
#define __RFB_EXCEPTION_H__
#include <rdr/Exception.h>
namespace rfb {
typedef rdr::Exception Exception;
struct AuthFailureException : public Exception {
AuthFailureException()
: Exception("Authentication failure") {}
AuthFailureException(const char* reason)
: Exception("Authentication failure: %s", reason) {}
};
struct AuthCancelledException : public rfb::Exception {
AuthCancelledException()
: Exception("Authentication cancelled") {}
};
struct ConnFailedException : public Exception {
ConnFailedException()
: Exception("Connection failed") {}
ConnFailedException(const char* reason)
: Exception("Connection failed: %s", reason) {}
};
}
#endif

424
common/rfb/HTTPServer.cxx Normal file
View File

@@ -0,0 +1,424 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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/HTTPServer.h>
#include <rfb/LogWriter.h>
#include <rfb/util.h>
#include <rdr/MemOutStream.h>
using namespace rfb;
using namespace rdr;
static LogWriter vlog("HTTPServer");
const int clientWaitTimeMillis = 20000;
const int idleTimeoutSecs = 5 * 60;
//
// -=- LineReader
// Helper class which is repeatedly called until a line has been read
// (lines end in \n or \r\n).
// Returns true when line complete, and resets internal state so that
// next read() call will start reading a new line.
// Only one buffer is kept - process line before reading next line!
//
class LineReader : public CharArray {
public:
LineReader(InStream& is_, int l)
: CharArray(l), is(is_), pos(0), len(l), bufferOverrun(false) {}
// Returns true if line complete, false otherwise
bool read() {
while (is.checkNoWait(1)) {
char c = is.readU8();
if (c == '\n') {
if (pos && (buf[pos-1] == '\r'))
pos--;
bufferOverrun = false;
buf[pos++] = 0;
pos = 0;
return true;
}
if (pos == (len-1)) {
bufferOverrun = true;
buf[pos] = 0;
return true;
}
buf[pos++] = c;
}
return false;
}
bool didBufferOverrun() const {return bufferOverrun;}
protected:
InStream& is;
int pos, len;
bool bufferOverrun;
};
//
// -=- HTTPServer::Session
// Manages the internal state for an HTTP session.
// processHTTP returns true when request has completed,
// indicating that socket & session data can be deleted.
//
class rfb::HTTPServer::Session {
public:
Session(network::Socket& s, rfb::HTTPServer& srv)
: contentType(0), contentLength(-1), lastModified(-1),
line(s.inStream(), 256), sock(s),
server(srv), state(ReadRequestLine), lastActive(time(0)) {
}
~Session() {
}
void writeResponse(int result, const char* text);
bool writeResponse(int code);
bool processHTTP();
network::Socket* getSock() const {return &sock;}
int checkIdleTimeout();
protected:
CharArray uri;
const char* contentType;
int contentLength;
time_t lastModified;
LineReader line;
network::Socket& sock;
rfb::HTTPServer& server;
enum {ReadRequestLine, ReadHeaders, WriteResponse} state;
enum {GetRequest, HeadRequest} request;
time_t lastActive;
};
// - Internal helper routines
void
copyStream(InStream& is, OutStream& os) {
try {
while (1) {
os.writeU8(is.readU8());
}
} catch (rdr::EndOfStream&) {
}
}
void writeLine(OutStream& os, const char* text) {
os.writeBytes(text, strlen(text));
os.writeBytes("\r\n", 2);
}
// - Write an HTTP-compliant response to the client
void
HTTPServer::Session::writeResponse(int result, const char* text) {
char buffer[1024];
if (strlen(text) > 512)
throw new rdr::Exception("Internal error - HTTP response text too big");
sprintf(buffer, "%s %d %s", "HTTP/1.1", result, text);
OutStream& os=sock.outStream();
writeLine(os, buffer);
writeLine(os, "Server: KasmVNC/4.0");
time_t now = time(0);
struct tm* tm = gmtime(&now);
strftime(buffer, 1024, "Date: %a, %d %b %Y %H:%M:%S GMT", tm);
writeLine(os, buffer);
if (lastModified == (time_t)-1 || lastModified == 0)
lastModified = now;
tm = gmtime(&lastModified);
strftime(buffer, 1024, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT", tm);
writeLine(os, buffer);
if (contentLength != -1) {
sprintf(buffer,"Content-Length: %d",contentLength);
writeLine(os, buffer);
}
writeLine(os, "Connection: close");
os.writeBytes("Content-Type: ", 14);
if (result == 200) {
if (!contentType)
contentType = guessContentType(uri.buf, "text/html");
os.writeBytes(contentType, strlen(contentType));
} else {
os.writeBytes("text/html", 9);
}
os.writeBytes("\r\n", 2);
writeLine(os, "");
if (result != 200) {
writeLine(os, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">");
writeLine(os, "<HTML><HEAD>");
sprintf(buffer, "<TITLE>%d %s</TITLE>", result, text);
writeLine(os, buffer);
writeLine(os, "</HEAD><BODY><H1>");
writeLine(os, text);
writeLine(os, "</H1></BODY></HTML>");
sock.outStream().flush();
}
}
bool
HTTPServer::Session::writeResponse(int code) {
switch (code) {
case 200: writeResponse(code, "OK"); break;
case 400: writeResponse(code, "Bad Request"); break;
case 404: writeResponse(code, "Not Found"); break;
case 501: writeResponse(code, "Not Implemented"); break;
default: writeResponse(500, "Unknown Error"); break;
};
// This return code is passed straight out of processHTTP().
// true indicates that the request has been completely processed.
return true;
}
// - Main HTTP request processing routine
bool
HTTPServer::Session::processHTTP() {
lastActive = time(0);
while (sock.inStream().checkNoWait(1)) {
switch (state) {
// Reading the Request-Line
case ReadRequestLine:
// Either read a line, or run out of incoming data
if (!line.read())
return false;
// We have read a line! Skip it if it's blank
if (strlen(line.buf) == 0)
continue;
// The line contains a request to process.
{
char method[16], path[128], version[16];
int matched = sscanf(line.buf, "%15s%127s%15s",
method, path, version);
if (matched != 3)
return writeResponse(400);
// Store the required "method"
if (strcmp(method, "GET") == 0)
request = GetRequest;
else if (strcmp(method, "HEAD") == 0)
request = HeadRequest;
else
return writeResponse(501);
// Store the URI to the "document"
uri.buf = strDup(path);
}
// Move on to reading the request headers
state = ReadHeaders;
break;
// Reading the request headers
case ReadHeaders:
// Try to read a line
if (!line.read())
return false;
// Skip headers until we hit a blank line
if (strlen(line.buf) != 0)
continue;
// Headers ended - write the response!
{
CharArray address(sock.getPeerAddress());
vlog.info("getting %s for %s", uri.buf, address.buf);
contentLength = -1;
lastModified = -1;
InStream* data = server.getFile(uri.buf, &contentType, &contentLength,
&lastModified);
if (!data)
return writeResponse(404);
try {
writeResponse(200);
if (request == GetRequest)
copyStream(*data, sock.outStream());
sock.outStream().flush();
} catch (rdr::Exception& e) {
vlog.error("error writing HTTP document:%s", e.str());
}
delete data;
}
// The operation is complete!
return true;
default:
throw rdr::Exception("invalid HTTPSession state!");
};
}
// Indicate that we're still processing the HTTP request.
return false;
}
int HTTPServer::Session::checkIdleTimeout() {
time_t now = time(0);
int timeout = (lastActive + idleTimeoutSecs) - now;
if (timeout > 0)
return secsToMillis(timeout);
sock.shutdown();
return 0;
}
// -=- Constructor / destructor
HTTPServer::HTTPServer() {
}
HTTPServer::~HTTPServer() {
std::list<Session*>::iterator i;
for (i=sessions.begin(); i!=sessions.end(); i++)
delete *i;
}
// -=- SocketServer interface implementation
void
HTTPServer::addSocket(network::Socket* sock, bool) {
Session* s = new Session(*sock, *this);
if (!s) {
sock->shutdown();
} else {
sock->inStream().setTimeout(clientWaitTimeMillis);
sock->outStream().setTimeout(clientWaitTimeMillis);
sessions.push_front(s);
}
}
void
HTTPServer::removeSocket(network::Socket* sock) {
std::list<Session*>::iterator i;
for (i=sessions.begin(); i!=sessions.end(); i++) {
if ((*i)->getSock() == sock) {
delete *i;
sessions.erase(i);
return;
}
}
}
void
HTTPServer::processSocketReadEvent(network::Socket* sock) {
std::list<Session*>::iterator i;
for (i=sessions.begin(); i!=sessions.end(); i++) {
if ((*i)->getSock() == sock) {
try {
if ((*i)->processHTTP()) {
vlog.info("completed HTTP request");
sock->shutdown();
}
} catch (rdr::Exception& e) {
vlog.error("untrapped: %s", e.str());
sock->shutdown();
}
return;
}
}
throw rdr::Exception("invalid Socket in HTTPServer");
}
void
HTTPServer::processSocketWriteEvent(network::Socket* sock) {
std::list<Session*>::iterator i;
for (i=sessions.begin(); i!=sessions.end(); i++) {
if ((*i)->getSock() == sock) {
try {
sock->outStream().flush();
} catch (rdr::Exception& e) {
vlog.error("untrapped: %s", e.str());
sock->shutdown();
}
return;
}
}
throw rdr::Exception("invalid Socket in HTTPServer");
}
void HTTPServer::getSockets(std::list<network::Socket*>* sockets)
{
sockets->clear();
std::list<Session*>::iterator ci;
for (ci = sessions.begin(); ci != sessions.end(); ci++) {
sockets->push_back((*ci)->getSock());
}
}
int HTTPServer::checkTimeouts() {
std::list<Session*>::iterator ci;
int timeout = 0;
for (ci = sessions.begin(); ci != sessions.end(); ci++) {
soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
}
return timeout;
}
// -=- Default getFile implementation
InStream*
HTTPServer::getFile(const char* name, const char** contentType,
int* contentLength, time_t* lastModified)
{
return 0;
}
const char*
HTTPServer::guessContentType(const char* name, const char* defType) {
CharArray file, ext;
if (!strSplit(name, '.', &file.buf, &ext.buf))
return defType;
if (strcasecmp(ext.buf, "html") == 0 ||
strcasecmp(ext.buf, "htm") == 0) {
return "text/html";
} else if (strcasecmp(ext.buf, "txt") == 0) {
return "text/plain";
} else if (strcasecmp(ext.buf, "gif") == 0) {
return "image/gif";
} else if (strcasecmp(ext.buf, "jpg") == 0) {
return "image/jpeg";
} else if (strcasecmp(ext.buf, "jar") == 0) {
return "application/java-archive";
} else if (strcasecmp(ext.buf, "exe") == 0) {
return "application/octet-stream";
}
return defType;
}

111
common/rfb/HTTPServer.h Normal file
View File

@@ -0,0 +1,111 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
// -=- HTTPServer.h
// Single-threaded HTTP server implementation.
// All I/O is handled by the processSocketEvent routine,
// which is called by the main-loop of the VNC server whenever
// there is an event on an HTTP socket.
#ifndef __RFB_HTTP_SERVER_H__
#define __RFB_HTTP_SERVER_H__
#include <rdr/MemInStream.h>
#include <rfb/UpdateTracker.h>
#include <rfb/Configuration.h>
#include <network/Socket.h>
#include <time.h>
namespace rfb {
class HTTPServer : public network::SocketServer {
public:
// -=- Constructors
// - HTTPServer(files)
// Create an HTTP server which will use the getFile method
// to satisfy HTTP GET requests.
HTTPServer();
virtual ~HTTPServer();
// SocketServer interface
// addSocket()
// This causes the server to perform HTTP protocol on the
// supplied socket.
virtual void addSocket(network::Socket* sock, bool outgoing=false);
// removeSocket()
// Could clean up socket-specific resources here.
virtual void removeSocket(network::Socket* sock);
// getSockets() gets a list of sockets. This can be used to generate an
// fd_set for calling select().
virtual void getSockets(std::list<network::Socket*>* sockets);
// processSocketReadEvent()
// The platform-specific side of the server implementation calls
// this method whenever data arrives on one of the active
// network sockets.
virtual void processSocketReadEvent(network::Socket* sock);
// processSocketWriteEvent()
// Similar to processSocketReadEvent(), but called when it is
// possible to write more data to a socket.
virtual void processSocketWriteEvent(network::Socket* sock);
// Check for socket timeouts
virtual int checkTimeouts();
// -=- File interface
// - getFile is passed the path portion of a URL and returns an
// InStream containing the data to return. If the requested
// file is available then the contentType should be set to the
// type of the file, or left untouched if the file type is to
// be determined automatically by HTTPServer.
// If the file is not available then null is returned.
// Overridden getFile functions should call the default version
// if they do not recognise a path name.
// NB: The caller assumes ownership of the returned InStream.
// NB: The contentType is statically allocated by the getFile impl.
// NB: contentType is *guaranteed* to be valid when getFile is called.
virtual rdr::InStream* getFile(const char* name, const char** contentType,
int* contentLength, time_t* lastModified);
// - guessContentType is passed the name of a file and returns the
// name of an HTTP content type, based on the file's extension. If
// the extension isn't recognised then defType is returned. This can
// be used from getFile to easily default to the supplied contentType,
// or by passing zero in to determine whether a type is recognised or
// not.
static const char* guessContentType(const char* name, const char* defType);
protected:
class Session;
std::list<Session*> sessions;
};
}
#endif

View File

@@ -0,0 +1,104 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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 <rdr/InStream.h>
#include <rdr/MemInStream.h>
#include <rdr/OutStream.h>
#include <rfb/ConnParams.h>
#include <rfb/PixelBuffer.h>
#include <rfb/HextileDecoder.h>
using namespace rfb;
#define BPP 8
#include <rfb/hextileDecode.h>
#undef BPP
#define BPP 16
#include <rfb/hextileDecode.h>
#undef BPP
#define BPP 32
#include <rfb/hextileDecode.h>
#undef BPP
HextileDecoder::HextileDecoder() : Decoder(DecoderPlain)
{
}
HextileDecoder::~HextileDecoder()
{
}
void HextileDecoder::readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os)
{
Rect t;
size_t bytesPerPixel;
bytesPerPixel = cp.pf().bpp/8;
for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
t.br.y = __rfbmin(r.br.y, t.tl.y + 16);
for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) {
rdr::U8 tileType;
t.br.x = __rfbmin(r.br.x, t.tl.x + 16);
tileType = is->readU8();
os->writeU8(tileType);
if (tileType & hextileRaw) {
os->copyBytes(is, t.area() * bytesPerPixel);
continue;
}
if (tileType & hextileBgSpecified)
os->copyBytes(is, bytesPerPixel);
if (tileType & hextileFgSpecified)
os->copyBytes(is, bytesPerPixel);
if (tileType & hextileAnySubrects) {
rdr::U8 nSubrects;
nSubrects = is->readU8();
os->writeU8(nSubrects);
if (tileType & hextileSubrectsColoured)
os->copyBytes(is, nSubrects * (bytesPerPixel + 2));
else
os->copyBytes(is, nSubrects * 2);
}
}
}
}
void HextileDecoder::decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
ModifiablePixelBuffer* pb)
{
rdr::MemInStream is(buffer, buflen);
const PixelFormat& pf = cp.pf();
switch (pf.bpp) {
case 8: hextileDecode8 (r, &is, pf, pb); break;
case 16: hextileDecode16(r, &is, pf, pb); break;
case 32: hextileDecode32(r, &is, pf, pb); break;
}
}

View File

@@ -0,0 +1,36 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
#ifndef __RFB_HEXTILEDECODER_H__
#define __RFB_HEXTILEDECODER_H__
#include <rfb/Decoder.h>
namespace rfb {
class HextileDecoder : public Decoder {
public:
HextileDecoder();
virtual ~HextileDecoder();
virtual void readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os);
virtual void decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
ModifiablePixelBuffer* pb);
};
}
#endif

View File

@@ -0,0 +1,106 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2005 Constantin Kaplinsky. 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 <rfb/encodings.h>
#include <rfb/SConnection.h>
#include <rfb/HextileEncoder.h>
#include <rfb/PixelBuffer.h>
#include <rfb/Configuration.h>
using namespace rfb;
BoolParameter improvedHextile("ImprovedHextile",
"Use improved compression algorithm for Hextile "
"encoding which achieves better compression "
"ratios by the cost of using more CPU time",
true);
#define BPP 8
#include <rfb/hextileEncode.h>
#include <rfb/hextileEncodeBetter.h>
#undef BPP
#define BPP 16
#include <rfb/hextileEncode.h>
#include <rfb/hextileEncodeBetter.h>
#undef BPP
#define BPP 32
#include <rfb/hextileEncode.h>
#include <rfb/hextileEncodeBetter.h>
#undef BPP
HextileEncoder::HextileEncoder(SConnection* conn) :
Encoder(conn, encodingHextile, EncoderPlain, -1)
{
}
HextileEncoder::~HextileEncoder()
{
}
bool HextileEncoder::isSupported()
{
return conn->cp.supportsEncoding(encodingHextile);
}
void HextileEncoder::writeRect(const PixelBuffer* pb, const Palette& palette)
{
rdr::OutStream* os = conn->getOutStream();
switch (pb->getPF().bpp) {
case 8:
if (improvedHextile) {
hextileEncodeBetter8(os, pb);
} else {
hextileEncode8(os, pb);
}
break;
case 16:
if (improvedHextile) {
hextileEncodeBetter16(os, pb);
} else {
hextileEncode16(os, pb);
}
break;
case 32:
if (improvedHextile) {
hextileEncodeBetter32(os, pb);
} else {
hextileEncode32(os, pb);
}
break;
}
}
void HextileEncoder::writeSolidRect(int width, int height,
const PixelFormat& pf,
const rdr::U8* colour)
{
rdr::OutStream* os;
int tiles;
os = conn->getOutStream();
tiles = ((width + 15)/16) * ((height + 15)/16);
os->writeU8(hextileBgSpecified);
os->writeBytes(colour, pf.bpp/8);
tiles--;
while (tiles--)
os->writeU8(0);
}

View File

@@ -0,0 +1,37 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. 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.
*/
#ifndef __RFB_HEXTILEENCODER_H__
#define __RFB_HEXTILEENCODER_H__
#include <rfb/Encoder.h>
namespace rfb {
class HextileEncoder : public Encoder {
public:
HextileEncoder(SConnection* conn);
virtual ~HextileEncoder();
virtual bool isSupported();
virtual void writeRect(const PixelBuffer* pb, const Palette& palette);
virtual void writeSolidRect(int width, int height,
const PixelFormat& pf,
const rdr::U8* colour);
};
}
#endif

100
common/rfb/Hostname.h Normal file
View File

@@ -0,0 +1,100 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
#ifndef __RFB_HOSTNAME_H__
#define __RFB_HOSTNAME_H__
#include <assert.h>
#include <stdlib.h>
#include <rdr/Exception.h>
#include <rfb/util.h>
namespace rfb {
static void getHostAndPort(const char* hi, char** host, int* port, int basePort=5900) {
const char* hostStart;
const char* hostEnd;
const char* portStart;
if (hi == NULL)
throw rdr::Exception("NULL host specified");
assert(host);
assert(port);
if (hi[0] == '[') {
hostStart = &hi[1];
hostEnd = strchr(hostStart, ']');
if (hostEnd == NULL)
throw rdr::Exception("unmatched [ in host");
portStart = hostEnd + 1;
if (*portStart == '\0')
portStart = NULL;
} else {
hostStart = &hi[0];
hostEnd = strrchr(hostStart, ':');
if (hostEnd == NULL) {
hostEnd = hostStart + strlen(hostStart);
portStart = NULL;
} else {
if ((hostEnd > hostStart) && (hostEnd[-1] == ':'))
hostEnd--;
portStart = strchr(hostStart, ':');
if (portStart != hostEnd) {
// We found more : in the host. This is probably an IPv6 address
hostEnd = hostStart + strlen(hostStart);
portStart = NULL;
}
}
}
if (hostStart == hostEnd)
*host = strDup("localhost");
else {
size_t len;
len = hostEnd - hostStart + 1;
*host = new char[len];
strncpy(*host, hostStart, len-1);
(*host)[len-1] = '\0';
}
if (portStart == NULL)
*port = basePort;
else {
char* end;
if (portStart[0] != ':')
throw rdr::Exception("invalid port specified");
if (portStart[1] != ':')
*port = strtol(portStart + 1, &end, 10);
else
*port = strtol(portStart + 2, &end, 10);
if (*end != '\0')
throw rdr::Exception("invalid port specified");
if ((portStart[1] != ':') && (*port < 100))
*port += basePort;
}
}
};
#endif // __RFB_HOSTNAME_H__

45
common/rfb/InputHandler.h Normal file
View File

@@ -0,0 +1,45 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
//
// InputHandler - abstract interface for accepting keyboard &
// pointer input and clipboard data.
//
#ifndef __RFB_INPUTHANDLER_H__
#define __RFB_INPUTHANDLER_H__
#include <rdr/types.h>
#include <rfb/Rect.h>
#include <rfb/util.h>
namespace rfb {
class InputHandler {
public:
virtual ~InputHandler() {}
virtual void keyEvent(rdr::U32 __unused_attr keysym,
rdr::U32 __unused_attr keycode,
bool __unused_attr down) { }
virtual void pointerEvent(const Point& __unused_attr pos,
int __unused_attr buttonMask) { }
virtual void clientCutText(const char* __unused_attr str,
int __unused_attr len) { }
};
}
#endif

View File

@@ -0,0 +1,254 @@
/* 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 <rfb/JpegCompressor.h>
#include <rdr/Exception.h>
#include <rfb/Rect.h>
#include <rfb/PixelFormat.h>
#include <rfb/ConnParams.h>
#include <stdio.h>
extern "C" {
#include <jpeglib.h>
}
#include <setjmp.h>
using namespace rfb;
//
// Special formats that libjpeg can have optimised code paths for
//
static const PixelFormat pfRGBX(32, 24, false, true, 255, 255, 255, 0, 8, 16);
static const PixelFormat pfBGRX(32, 24, false, true, 255, 255, 255, 16, 8, 0);
static const PixelFormat pfXRGB(32, 24, false, true, 255, 255, 255, 8, 16, 24);
static const PixelFormat pfXBGR(32, 24, false, true, 255, 255, 255, 24, 16, 8);
//
// Error manager implementation for the JPEG library
//
struct JPEG_ERROR_MGR {
struct jpeg_error_mgr pub;
jmp_buf jmpBuffer;
char lastError[JMSG_LENGTH_MAX];
};
static void
JpegErrorExit(j_common_ptr cinfo)
{
JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)cinfo->err;
(*cinfo->err->output_message)(cinfo);
longjmp(err->jmpBuffer, 1);
}
static void
JpegOutputMessage(j_common_ptr cinfo)
{
JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)cinfo->err;
(*cinfo->err->format_message)(cinfo, err->lastError);
}
//
// Destination manager implementation for the JPEG library.
//
struct JPEG_DEST_MGR {
struct jpeg_destination_mgr pub;
JpegCompressor *instance;
};
static void
JpegInitDestination(j_compress_ptr cinfo)
{
JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest;
JpegCompressor *jc = dest->instance;
jc->clear();
dest->pub.next_output_byte = jc->getptr();
dest->pub.free_in_buffer = jc->getend() - jc->getptr();
}
static boolean
JpegEmptyOutputBuffer(j_compress_ptr cinfo)
{
JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest;
JpegCompressor *jc = dest->instance;
jc->setptr(jc->getend());
jc->overrun(jc->getend() - jc->getstart(), 1);
dest->pub.next_output_byte = jc->getptr();
dest->pub.free_in_buffer = jc->getend() - jc->getptr();
return TRUE;
}
static void
JpegTermDestination(j_compress_ptr cinfo)
{
JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest;
JpegCompressor *jc = dest->instance;
jc->setptr(dest->pub.next_output_byte);
}
JpegCompressor::JpegCompressor(int bufferLen) : MemOutStream(bufferLen)
{
cinfo = new jpeg_compress_struct;
err = new struct JPEG_ERROR_MGR;
cinfo->err = jpeg_std_error(&err->pub);
snprintf(err->lastError, JMSG_LENGTH_MAX, "No error");
err->pub.error_exit = JpegErrorExit;
err->pub.output_message = JpegOutputMessage;
if(setjmp(err->jmpBuffer)) {
// this will execute if libjpeg has an error
throw rdr::Exception("%s", err->lastError);
}
jpeg_create_compress(cinfo);
dest = new struct JPEG_DEST_MGR;
dest->pub.init_destination = JpegInitDestination;
dest->pub.empty_output_buffer = JpegEmptyOutputBuffer;
dest->pub.term_destination = JpegTermDestination;
dest->instance = this;
cinfo->dest = (struct jpeg_destination_mgr *)dest;
}
JpegCompressor::~JpegCompressor(void)
{
if(setjmp(err->jmpBuffer)) {
// this will execute if libjpeg has an error
return;
}
jpeg_destroy_compress(cinfo);
delete err;
delete dest;
delete cinfo;
}
void JpegCompressor::compress(const rdr::U8 *buf, int stride, const Rect& r,
const PixelFormat& pf, int quality, int subsamp)
{
int w = r.width();
int h = r.height();
int pixelsize;
rdr::U8 *srcBuf = NULL;
bool srcBufIsTemp = false;
JSAMPROW *rowPointer = NULL;
if(setjmp(err->jmpBuffer)) {
// this will execute if libjpeg has an error
jpeg_abort_compress(cinfo);
if (srcBufIsTemp && srcBuf) delete[] srcBuf;
if (rowPointer) delete[] rowPointer;
throw rdr::Exception("%s", err->lastError);
}
cinfo->image_width = w;
cinfo->image_height = h;
cinfo->in_color_space = JCS_RGB;
pixelsize = 3;
#ifdef JCS_EXTENSIONS
// Try to have libjpeg output directly to our native format
// libjpeg can only handle some "standard" formats
if (pfRGBX.equal(pf))
cinfo->in_color_space = JCS_EXT_RGBX;
else if (pfBGRX.equal(pf))
cinfo->in_color_space = JCS_EXT_BGRX;
else if (pfXRGB.equal(pf))
cinfo->in_color_space = JCS_EXT_XRGB;
else if (pfXBGR.equal(pf))
cinfo->in_color_space = JCS_EXT_XBGR;
if (cinfo->in_color_space != JCS_RGB) {
srcBuf = (rdr::U8 *)buf;
pixelsize = 4;
}
#endif
if (stride == 0)
stride = w;
if (cinfo->in_color_space == JCS_RGB) {
srcBuf = new rdr::U8[w * h * pixelsize];
srcBufIsTemp = true;
pf.rgbFromBuffer(srcBuf, (const rdr::U8 *)buf, w, stride, h);
stride = w;
}
cinfo->input_components = pixelsize;
jpeg_set_defaults(cinfo);
if (quality >= 1 && quality <= 100) {
jpeg_set_quality(cinfo, quality, TRUE);
if (quality >= 96)
cinfo->dct_method = JDCT_ISLOW;
else
cinfo->dct_method = JDCT_FASTEST;
}
switch (subsamp) {
case subsample16X:
case subsample8X:
// FIXME (fall through)
case subsample4X:
cinfo->comp_info[0].h_samp_factor = 2;
cinfo->comp_info[0].v_samp_factor = 2;
break;
case subsample2X:
cinfo->comp_info[0].h_samp_factor = 2;
cinfo->comp_info[0].v_samp_factor = 1;
break;
case subsampleGray:
jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
default:
cinfo->comp_info[0].h_samp_factor = 1;
cinfo->comp_info[0].v_samp_factor = 1;
}
rowPointer = new JSAMPROW[h];
for (int dy = 0; dy < h; dy++)
rowPointer[dy] = (JSAMPROW)(&srcBuf[dy * stride * pixelsize]);
jpeg_start_compress(cinfo, TRUE);
while (cinfo->next_scanline < cinfo->image_height)
jpeg_write_scanlines(cinfo, &rowPointer[cinfo->next_scanline],
cinfo->image_height - cinfo->next_scanline);
jpeg_finish_compress(cinfo);
if (srcBufIsTemp) delete[] srcBuf;
delete[] rowPointer;
}
void JpegCompressor::writeBytes(const void* data, int length)
{
throw rdr::Exception("writeBytes() is not valid with a JpegCompressor instance. Use compress() instead.");
}

View File

@@ -0,0 +1,67 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
*
* 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.
*/
//
// JpegCompressor compresses RGB input into a JPEG image and stores it in
// an underlying MemOutStream
//
#ifndef __RFB_JPEGCOMPRESSOR_H__
#define __RFB_JPEGCOMPRESSOR_H__
#include <rdr/MemOutStream.h>
#include <rfb/PixelFormat.h>
#include <rfb/Rect.h>
struct jpeg_compress_struct;
struct JPEG_ERROR_MGR;
struct JPEG_DEST_MGR;
namespace rfb {
class JpegCompressor : public rdr::MemOutStream {
public:
JpegCompressor(int bufferLen = 128*1024);
virtual ~JpegCompressor();
void compress(const rdr::U8 *, int, const Rect&, const PixelFormat&, int, int);
void writeBytes(const void*, int);
inline rdr::U8* getstart() { return start; }
inline int overrun(int itemSize, int nItems) {
return MemOutStream::overrun(itemSize, nItems);
}
private:
struct jpeg_compress_struct *cinfo;
struct JPEG_ERROR_MGR *err;
struct JPEG_DEST_MGR *dest;
};
} // end of namespace rfb
#endif

View File

@@ -0,0 +1,229 @@
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
* Copyright (C) 2004-2005 Cendio AB. 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 <rfb/JpegDecompressor.h>
#include <rdr/Exception.h>
#include <rfb/Rect.h>
#include <rfb/PixelFormat.h>
#include <stdio.h>
extern "C" {
#include <jpeglib.h>
}
#include <jerror.h>
#include <setjmp.h>
using namespace rfb;
//
// Special formats that libjpeg can have optimised code paths for
//
static const PixelFormat pfRGBX(32, 24, false, true, 255, 255, 255, 0, 8, 16);
static const PixelFormat pfBGRX(32, 24, false, true, 255, 255, 255, 16, 8, 0);
static const PixelFormat pfXRGB(32, 24, false, true, 255, 255, 255, 8, 16, 24);
static const PixelFormat pfXBGR(32, 24, false, true, 255, 255, 255, 24, 16, 8);
//
// Error manager implementation for the JPEG library
//
struct JPEG_ERROR_MGR {
struct jpeg_error_mgr pub;
jmp_buf jmpBuffer;
char lastError[JMSG_LENGTH_MAX];
};
static void
JpegErrorExit(j_common_ptr dinfo)
{
JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)dinfo->err;
(*dinfo->err->output_message)(dinfo);
longjmp(err->jmpBuffer, 1);
}
static void
JpegOutputMessage(j_common_ptr dinfo)
{
JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)dinfo->err;
(*dinfo->err->format_message)(dinfo, err->lastError);
}
//
// Source manager implementation for the JPEG library.
//
struct JPEG_SRC_MGR {
struct jpeg_source_mgr pub;
JpegDecompressor *instance;
};
static void
JpegNoOp(j_decompress_ptr dinfo)
{
}
static boolean
JpegFillInputBuffer(j_decompress_ptr dinfo)
{
ERREXIT(dinfo, JERR_BUFFER_SIZE);
return TRUE;
}
static void
JpegSkipInputData(j_decompress_ptr dinfo, long num_bytes)
{
JPEG_SRC_MGR *src = (JPEG_SRC_MGR *)dinfo->src;
if (num_bytes < 0 || (size_t)num_bytes > src->pub.bytes_in_buffer) {
ERREXIT(dinfo, JERR_BUFFER_SIZE);
} else {
src->pub.next_input_byte += (size_t) num_bytes;
src->pub.bytes_in_buffer -= (size_t) num_bytes;
}
}
JpegDecompressor::JpegDecompressor(void)
{
dinfo = new jpeg_decompress_struct;
err = new struct JPEG_ERROR_MGR;
dinfo->err = jpeg_std_error(&err->pub);
snprintf(err->lastError, JMSG_LENGTH_MAX, "No error");
err->pub.error_exit = JpegErrorExit;
err->pub.output_message = JpegOutputMessage;
if(setjmp(err->jmpBuffer)) {
// this will execute if libjpeg has an error
throw rdr::Exception("%s", err->lastError);
}
jpeg_create_decompress(dinfo);
src = new struct JPEG_SRC_MGR;
src->pub.init_source = JpegNoOp;
src->pub.fill_input_buffer = JpegFillInputBuffer;
src->pub.skip_input_data = JpegSkipInputData;
src->pub.resync_to_restart = jpeg_resync_to_restart;
src->pub.term_source = JpegNoOp;
src->instance = this;
dinfo->src = (struct jpeg_source_mgr *)src;
}
JpegDecompressor::~JpegDecompressor(void)
{
if(setjmp(err->jmpBuffer)) {
// this will execute if libjpeg has an error
return;
}
jpeg_destroy_decompress(dinfo);
delete err;
delete src;
delete dinfo;
}
void JpegDecompressor::decompress(const rdr::U8 *jpegBuf, int jpegBufLen,
rdr::U8 *buf, int stride, const Rect& r, const PixelFormat& pf)
{
int w = r.width();
int h = r.height();
int pixelsize;
int dstBufStride;
rdr::U8 *dstBuf = NULL;
bool dstBufIsTemp = false;
JSAMPROW *rowPointer = NULL;
if(setjmp(err->jmpBuffer)) {
// this will execute if libjpeg has an error
jpeg_abort_decompress(dinfo);
if (dstBufIsTemp && dstBuf) delete[] dstBuf;
if (rowPointer) delete[] rowPointer;
throw rdr::Exception("%s", err->lastError);
}
src->pub.next_input_byte = jpegBuf;
src->pub.bytes_in_buffer = jpegBufLen;
jpeg_read_header(dinfo, TRUE);
dinfo->out_color_space = JCS_RGB;
pixelsize = 3;
if (stride == 0)
stride = w;
dstBufStride = stride;
#ifdef JCS_EXTENSIONS
// Try to have libjpeg output directly to our native format
// libjpeg can only handle some "standard" formats
if (pfRGBX.equal(pf))
dinfo->out_color_space = JCS_EXT_RGBX;
else if (pfBGRX.equal(pf))
dinfo->out_color_space = JCS_EXT_BGRX;
else if (pfXRGB.equal(pf))
dinfo->out_color_space = JCS_EXT_XRGB;
else if (pfXBGR.equal(pf))
dinfo->out_color_space = JCS_EXT_XBGR;
if (dinfo->out_color_space != JCS_RGB) {
dstBuf = (rdr::U8 *)buf;
pixelsize = 4;
}
#endif
if (dinfo->out_color_space == JCS_RGB) {
dstBuf = new rdr::U8[w * h * pixelsize];
dstBufIsTemp = true;
dstBufStride = w;
}
rowPointer = new JSAMPROW[h];
for (int dy = 0; dy < h; dy++)
rowPointer[dy] = (JSAMPROW)(&dstBuf[dy * dstBufStride * pixelsize]);
jpeg_start_decompress(dinfo);
if (dinfo->output_width != (unsigned)r.width()
|| dinfo->output_height != (unsigned)r.height()
|| dinfo->output_components != pixelsize) {
jpeg_abort_decompress(dinfo);
if (dstBufIsTemp && dstBuf) delete[] dstBuf;
if (rowPointer) delete[] rowPointer;
throw rdr::Exception("Tight Decoding: Wrong JPEG data received.\n");
}
while (dinfo->output_scanline < dinfo->output_height) {
jpeg_read_scanlines(dinfo, &rowPointer[dinfo->output_scanline],
dinfo->output_height - dinfo->output_scanline);
}
if (dinfo->out_color_space == JCS_RGB)
pf.bufferFromRGB((rdr::U8*)buf, dstBuf, w, stride, h);
jpeg_finish_decompress(dinfo);
if (dstBufIsTemp) delete [] dstBuf;
delete[] rowPointer;
}

View File

@@ -0,0 +1,60 @@
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
* Copyright (C) 2004-2005 Cendio AB. All rights reserved.
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
*
* 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.
*/
//
// JpegDecompressor decompresses a JPEG image into RGB output
// an underlying MemOutStream
//
#ifndef __RFB_JPEGDECOMPRESSOR_H__
#define __RFB_JPEGDECOMPRESSOR_H__
#include <rfb/PixelFormat.h>
#include <rfb/Rect.h>
struct jpeg_decompress_struct;
struct JPEG_ERROR_MGR;
struct JPEG_SRC_MGR;
namespace rfb {
class JpegDecompressor {
public:
JpegDecompressor(void);
virtual ~JpegDecompressor();
void decompress(const rdr::U8 *, int, rdr::U8 *, int, const Rect&,
const PixelFormat&);
private:
struct jpeg_decompress_struct *dinfo;
struct JPEG_ERROR_MGR *err;
struct JPEG_SRC_MGR *src;
};
} // end of namespace rfb
#endif

View File

@@ -0,0 +1,93 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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 <os/Mutex.h>
#include <rfb/KeyRemapper.h>
#include <rfb/Configuration.h>
#include <rfb/LogWriter.h>
using namespace rfb;
static LogWriter vlog("KeyRemapper");
KeyRemapper KeyRemapper::defInstance;
KeyRemapper::KeyRemapper(const char* m)
{
mutex = new os::Mutex;
setMapping(m);
}
KeyRemapper::~KeyRemapper()
{
delete mutex;
}
void KeyRemapper::setMapping(const char* m) {
os::AutoMutex a(mutex);
mapping.clear();
while (m[0]) {
int from, to;
char bidi;
const char* nextComma = strchr(m, ',');
if (!nextComma)
nextComma = m + strlen(m);
if (sscanf(m, "0x%x%c>0x%x", &from,
&bidi, &to) == 3) {
if (bidi != '-' && bidi != '<')
vlog.error("warning: unknown operation %c>, assuming ->", bidi);
mapping[from] = to;
if (bidi == '<')
mapping[to] = from;
} else {
vlog.error("warning: bad mapping %.*s", (int)(nextComma-m), m);
}
m = nextComma;
if (nextComma[0])
m++;
}
}
rdr::U32 KeyRemapper::remapKey(rdr::U32 key) const {
os::AutoMutex a(mutex);
std::map<rdr::U32,rdr::U32>::const_iterator i = mapping.find(key);
if (i != mapping.end())
return i->second;
return key;
}
class KeyMapParameter : public StringParameter {
public:
KeyMapParameter()
: StringParameter("RemapKeys", "Comma-separated list of incoming keysyms to remap. Mappings are expressed as two hex values, prefixed by 0x, and separated by ->", "") {
setParam(value);
}
bool setParam(const char* v) {
KeyRemapper::defInstance.setMapping(v);
return StringParameter::setParam(v);
}
} defaultParam;

43
common/rfb/KeyRemapper.h Normal file
View File

@@ -0,0 +1,43 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
#ifndef __RFB_KEYREMAPPER_H__
#define __RFB_KEYREMAPPER_H__
#include <map>
#include <rdr/types.h>
namespace os { class Mutex; }
namespace rfb {
class KeyRemapper {
public:
KeyRemapper(const char* m="");
~KeyRemapper();
void setMapping(const char* m);
rdr::U32 remapKey(rdr::U32 key) const;
static KeyRemapper defInstance;
private:
std::map<rdr::U32,rdr::U32> mapping;
os::Mutex* mutex;
};
};
#endif // __RFB_KEYREMAPPER_H__

126
common/rfb/ListConnInfo.h Normal file
View File

@@ -0,0 +1,126 @@
/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
#ifndef __RFB_LISTCONNINFO_INCLUDED__
#define __RFB_LISTCONNINFO_INCLUDED__
#include <list>
#include <rfb/util.h>
namespace rfb {
struct ListConnInfo {
ListConnInfo() : disableClients(false) {}
void Clear() {
conn.clear();
IP_address.clear();
time_conn.clear();
status.clear();
}
bool Empty() { return conn.empty();}
void iBegin() {
ci = conn.begin();
Ii = IP_address.begin();
ti = time_conn.begin();
si = status.begin();
}
bool iEnd() { return ci == conn.end();}
void iNext() {
ci++;
Ii++;
ti++;
si++;
}
void addInfo(void* Conn, char* IP, char* Time, int Status) {
conn.push_back(Conn);
IP_address.push_back(strDup(IP));
time_conn.push_back(strDup(Time));
status.push_back(Status);
}
void iGetCharInfo(char* buf[3]) {
buf[0] = *Ii;
buf[1] = *ti;
switch (*si) {
case 0:
buf[2] = strDup("Full control");
break;
case 1:
buf[2] = strDup("View only");
break;
case 2:
buf[2] = strDup("Stop updating");
break;
default:
buf[2] = strDup("Unknown");
}
}
void* iGetConn() { return *ci;}
int iGetStatus() { return *si;}
void iSetStatus(int istatus) { *si = istatus;}
void Copy(ListConnInfo* InputList) {
Clear();
if (InputList->Empty()) return;
for (InputList->iBegin(); !InputList->iEnd(); InputList->iNext()) {
iAdd(InputList);
}
setDisable(InputList->getDisable());
}
void iAdd (ListConnInfo* InputList) {
char* buf[3];
InputList->iGetCharInfo(buf);
addInfo(InputList->iGetConn(), buf[0], buf[1], InputList->iGetStatus());
}
void setDisable(bool disable) {disableClients = disable;}
bool getDisable() {return disableClients;}
void setAllStatus(int stat) {
std::list<int>::iterator st;
for (st = status.begin(); st != status.end(); st++)
*st = stat;
}
private:
std::list<void*> conn;
std::list<char*> IP_address;
std::list<char*> time_conn;
std::list<int> status;
std::list<void*>::iterator ci;
std::list<char*>::iterator Ii;
std::list<char*>::iterator ti;
std::list<int>::iterator si;
bool disableClients;
};
};
#endif

134
common/rfb/LogWriter.cxx Normal file
View File

@@ -0,0 +1,134 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
// -=- LogWriter.cxx - client-side logging interface
#include <string.h>
#include <rfb/LogWriter.h>
#include <rfb/Configuration.h>
#include <rfb/util.h>
#include <stdlib.h>
rfb::LogParameter rfb::logParams;
using namespace rfb;
LogWriter::LogWriter(const char* name) : m_name(name), m_level(0), m_log(0), m_next(log_writers) {
log_writers = this;
}
LogWriter::~LogWriter() {
// *** Should remove this logger here!
}
void LogWriter::setLog(Logger *logger) {
m_log = logger;
}
void LogWriter::setLevel(int level) {
m_level = level;
}
void
LogWriter::listLogWriters(int width) {
// *** make this respect width...
LogWriter* current = log_writers;
fprintf(stderr, " ");
while (current) {
fprintf(stderr, "%s", current->m_name);
current = current->m_next;
if (current) fprintf(stderr, ", ");
}
fprintf(stderr, "\n");
}
LogWriter* LogWriter::log_writers;
LogWriter*
LogWriter::getLogWriter(const char* name) {
LogWriter* current = log_writers;
while (current) {
if (strcasecmp(name, current->m_name) == 0) return current;
current = current->m_next;
}
return 0;
}
bool LogWriter::setLogParams(const char* params) {
CharArray logwriterName, loggerName, logLevel;
if (!strSplit(params, ':', &logwriterName.buf, &loggerName.buf) ||
!strSplit(loggerName.buf, ':', &loggerName.buf, &logLevel.buf)) {
fprintf(stderr,"failed to parse log params:%s\n",params);
return false;
}
int level = atoi(logLevel.buf);
Logger* logger = 0;
if (strcmp("", loggerName.buf) != 0) {
logger = Logger::getLogger(loggerName.buf);
if (!logger) fprintf(stderr,"no logger found! %s\n",loggerName.buf);
}
if (strcmp("*", logwriterName.buf) == 0) {
LogWriter* current = log_writers;
while (current) {
current->setLog(logger);
current->setLevel(level);
current = current->m_next;
}
return true;
} else {
LogWriter* logwriter = getLogWriter(logwriterName.buf);
if (!logwriter) {
fprintf(stderr,"no logwriter found! %s\n",logwriterName.buf);
} else {
logwriter->setLog(logger);
logwriter->setLevel(level);
return true;
}
}
return false;
}
LogParameter::LogParameter()
: StringParameter("Log",
"Specifies which log output should be directed to "
"which target logger, and the level of output to log. "
"Format is <log>:<target>:<level>[, ...].",
"") {
}
bool LogParameter::setParam(const char* v) {
if (immutable) return true;
LogWriter::setLogParams("*::0");
StringParameter::setParam(v);
CharArray logParam;
CharArray params(getData());
while (params.buf) {
strSplit(params.buf, ',', &logParam.buf, &params.buf);
if (strlen(logParam.buf) && !LogWriter::setLogParams(logParam.buf))
return false;
}
return true;
}
void LogParameter::setDefault(const char* d) {
def_value = d;
setParam(def_value);
}

122
common/rfb/LogWriter.h Normal file
View File

@@ -0,0 +1,122 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
// -=- LogWriter.h - The Log writer class.
#ifndef __RFB_LOG_WRITER_H__
#define __RFB_LOG_WRITER_H__
#include <stdarg.h>
#include <rfb/Logger.h>
#include <rfb/Configuration.h>
#ifdef __GNUC__
# define __printf_attr(a, b) __attribute__((__format__ (__printf__, a, b)))
#else
# define __printf_attr(a, b)
#endif // __GNUC__
// Each log writer instance has a unique textual name,
// and is attached to a particular Log instance and
// is assigned a particular log level.
#define DEF_LOGFUNCTION(name, level) \
inline void v##name(const char* fmt, va_list ap) __printf_attr(2, 0) { \
if (m_log && (level <= m_level)) \
m_log->write(level, m_name, fmt, ap); \
} \
inline void name(const char* fmt, ...) __printf_attr(2, 3) { \
if (m_log && (level <= m_level)) { \
va_list ap; va_start(ap, fmt); \
m_log->write(level, m_name, fmt, ap);\
va_end(ap); \
} \
}
namespace rfb {
class LogWriter;
class LogWriter {
public:
LogWriter(const char* name);
~LogWriter();
const char *getName() {return m_name;}
void setLog(Logger *logger);
void setLevel(int level);
int getLevel(void) { return m_level; }
inline void write(int level, const char* format, ...) __printf_attr(3, 4) {
if (m_log && (level <= m_level)) {
va_list ap;
va_start(ap, format);
m_log->write(level, m_name, format, ap);
va_end(ap);
}
}
static const int LEVEL_ERROR = 0;
static const int LEVEL_STATUS = 10;
static const int LEVEL_INFO = 30;
static const int LEVEL_DEBUG = 100;
DEF_LOGFUNCTION(error, LEVEL_ERROR)
DEF_LOGFUNCTION(status, LEVEL_STATUS)
DEF_LOGFUNCTION(info, LEVEL_INFO)
DEF_LOGFUNCTION(debug, LEVEL_DEBUG)
// -=- DIAGNOSTIC & HELPER ROUTINES
static void listLogWriters(int width=79);
// -=- CLASS FIELDS & FUNCTIONS
static LogWriter* log_writers;
static LogWriter* getLogWriter(const char* name);
static bool setLogParams(const char* params);
private:
const char* m_name;
int m_level;
Logger* m_log;
LogWriter* m_next;
};
class LogParameter : public StringParameter {
public:
LogParameter();
virtual bool setParam(const char* v);
// Call this to set a suitable default value.
// Can't use the normal default mechanism for
// this because there is no guarantee on C++
// constructor ordering - some LogWriters may
// not exist when LogParameter gets constructed.
// NB: The default value must exist for the
// lifetime of the process!
void setDefault(const char* v);
};
extern LogParameter logParams;
};
#endif // __RFB_LOG_WRITER_H__

81
common/rfb/Logger.cxx Normal file
View File

@@ -0,0 +1,81 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
// -=- Logger.cxx - support for the Logger and LogWriter classes
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <rfb/Logger.h>
#include <rfb/LogWriter.h>
#include <rfb/util.h>
using namespace rfb;
Logger* Logger::loggers = 0;
Logger::Logger(const char* name) : registered(false), m_name(name), m_next(0) {
}
Logger::~Logger() {
// *** Should remove this logger here!
}
void Logger::write(int level, const char *logname, const char* format,
va_list ap)
{
// - Format the supplied data, and pass it to the
// actual log_message function
// The log level is included as a hint for loggers capable of representing
// different log levels in some way.
char buf1[4096];
vsnprintf(buf1, sizeof(buf1)-1, format, ap);
buf1[sizeof(buf1)-1] = 0;
write(level, logname, buf1);
}
void
Logger::registerLogger() {
if (!registered) {
registered = true;
m_next = loggers;
loggers=this;
}
}
Logger*
Logger::getLogger(const char* name) {
Logger* current = loggers;
while (current) {
if (strcasecmp(name, current->m_name) == 0) return current;
current = current->m_next;
}
return 0;
}
void
Logger::listLoggers() {
Logger* current = loggers;
while (current) {
printf(" %s\n", current->m_name);
current = current->m_next;
}
}

76
common/rfb/Logger.h Normal file
View File

@@ -0,0 +1,76 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
// -=- Logger.h - The Logger class.
#ifndef __RFB_LOGGER_H__
#define __RFB_LOGGER_H__
#include <stdarg.h>
#include <stdio.h>
// Each log writer instance has a unique textual name,
// and is attached to a particular Logger instance and
// is assigned a particular log level.
#ifdef __GNUC__
# define __printf_attr(a, b) __attribute__((__format__ (__printf__, a, b)))
#else
# define __printf_attr(a, b)
#endif // __GNUC__
namespace rfb {
class Logger {
public:
// -=- Create / Destroy a logger
Logger(const char* name);
virtual ~Logger();
// -=- Get the name of a logger
const char *getName() {return m_name;}
// -=- Write data to a log
virtual void write(int level, const char *logname, const char *text) = 0;
void write(int level, const char *logname, const char* format, va_list ap) __printf_attr(4, 0);
// -=- Register a logger
void registerLogger();
// -=- CLASS FIELDS & FUNCTIONS
static Logger* loggers;
static Logger* getLogger(const char* name);
static void listLoggers();
private:
bool registered;
const char *m_name;
Logger *m_next;
};
};
#endif // __RFB_LOGGER_H__

120
common/rfb/Logger_file.cxx Normal file
View File

@@ -0,0 +1,120 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
// -=- Logger_file.cxx - Logger instance for a file
#include <stdlib.h>
#include <string.h>
#include <os/Mutex.h>
#include <rfb/util.h>
#include <rfb/Logger_file.h>
using namespace rfb;
Logger_File::Logger_File(const char* loggerName)
: Logger(loggerName), indent(13), width(79), m_filename(0), m_file(0),
m_lastLogTime(0)
{
mutex = new os::Mutex();
}
Logger_File::~Logger_File()
{
closeFile();
delete mutex;
}
void Logger_File::write(int level, const char *logname, const char *message)
{
os::AutoMutex a(mutex);
if (!m_file) {
if (!m_filename) return;
CharArray bakFilename(strlen(m_filename) + 1 + 4);
sprintf(bakFilename.buf, "%s.bak", m_filename);
remove(bakFilename.buf);
rename(m_filename, bakFilename.buf);
m_file = fopen(m_filename, "w+");
if (!m_file) return;
}
time_t current = time(0);
if (current != m_lastLogTime) {
m_lastLogTime = current;
// fprintf(m_file, "\n%s", ctime(&m_lastLogTime));
}
fprintf(m_file," %s:", logname);
int column = strlen(logname) + 2;
if (column < indent) {
fprintf(m_file,"%*s",indent-column,"");
column = indent;
}
/*while (true) {
const char* s = strchr(message, ' ');
int wordLen;
if (s) wordLen = s-message;
else wordLen = strlen(message);
if (column + wordLen + 1 > width) {
fprintf(m_file,"\n%*s",indent,"");
column = indent;
}
fprintf(m_file," %.*s",wordLen,message);
column += wordLen + 1;
message += wordLen + 1;
if (!s) break;
}*/
fprintf(m_file," %s",message);
fprintf(m_file,"\n");
fflush(m_file);
}
void Logger_File::setFilename(const char* filename)
{
closeFile();
m_filename = strDup(filename);
}
void Logger_File::setFile(FILE* file)
{
closeFile();
m_file = file;
}
void Logger_File::closeFile()
{
if (m_filename) {
if (m_file) {
fclose(m_file);
m_file = 0;
}
strFree(m_filename);
m_filename = 0;
}
}
static Logger_File logger("file");
bool rfb::initFileLogger(const char* filename) {
logger.setFilename(filename);
logger.registerLogger();
return true;
}

54
common/rfb/Logger_file.h Normal file
View File

@@ -0,0 +1,54 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
// -=- Logger_file - log to a file
#ifndef __RFB_LOGGER_FILE_H__
#define __RFB_LOGGER_FILE_H__
#include <time.h>
#include <rfb/Logger.h>
namespace os { class Mutex; }
namespace rfb {
class Logger_File : public Logger {
public:
Logger_File(const char* loggerName);
~Logger_File();
virtual void write(int level, const char *logname, const char *message);
void setFilename(const char* filename);
void setFile(FILE* file);
int indent;
int width;
protected:
void closeFile();
char* m_filename;
FILE* m_file;
time_t m_lastLogTime;
os::Mutex* mutex;
};
bool initFileLogger(const char* filename);
};
#endif

View File

@@ -0,0 +1,32 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
// -=- Logger_stdio.cxx - Logger instances for stderr and stdout
#include <rfb/Logger_stdio.h>
using namespace rfb;
static Logger_StdIO logStdErr("stderr", stderr);
static Logger_StdIO logStdOut("stdout", stdout);
bool rfb::initStdIOLoggers() {
logStdErr.registerLogger();
logStdOut.registerLogger();
return true;
}

39
common/rfb/Logger_stdio.h Normal file
View File

@@ -0,0 +1,39 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
// -=- Logger_stdio - standard output logger instances
#ifndef __RFB_LOGGER_STDIO_H__
#define __RFB_LOGGER_STDIO_H__
#include <rfb/Logger_file.h>
namespace rfb {
class Logger_StdIO : public Logger_File {
public:
Logger_StdIO(const char *name, FILE* file) : Logger_File(name) {
setFile(file);
}
};
bool initStdIOLoggers();
};
#endif

View File

@@ -0,0 +1,64 @@
/* Copyright (C) 2015 TigerVNC
*
* 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.
*/
// -=- Logger_syslog.cxx - Logger instance for a syslog
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <rfb/util.h>
#include <rfb/Logger_syslog.h>
#include <rfb/LogWriter.h>
using namespace rfb;
Logger_Syslog::Logger_Syslog(const char* loggerName)
: Logger(loggerName)
{
openlog(0, LOG_CONS | LOG_PID, LOG_USER);
}
Logger_Syslog::~Logger_Syslog()
{
closelog();
}
void Logger_Syslog::write(int level, const char *logname, const char *message)
{
// Convert our priority level into syslog level
int priority;
if (level >= LogWriter::LEVEL_DEBUG) {
priority = LOG_DEBUG;
} else if (level >= LogWriter::LEVEL_INFO) {
priority = LOG_INFO;
} else if (level >= LogWriter::LEVEL_STATUS) {
priority = LOG_NOTICE;
} else {
priority = LOG_ERR;
}
syslog(priority, "%s: %s", logname, message);
}
static Logger_Syslog logger("syslog");
void rfb::initSyslogLogger() {
logger.registerLogger();
}

View File

@@ -0,0 +1,40 @@
/* Copyright (C) 2015 TigerVNC
*
* 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.
*/
// -=- Logger_syslog - log to syslog
#ifndef __RFB_LOGGER_SYSLOG_H__
#define __RFB_LOGGER_SYSLOG_H__
#include <time.h>
#include <rfb/Logger.h>
namespace rfb {
class Logger_Syslog : public Logger {
public:
Logger_Syslog(const char* loggerName);
virtual ~Logger_Syslog();
virtual void write(int level, const char *logname, const char *message);
};
void initSyslogLogger();
};
#endif

190
common/rfb/Palette.h Normal file
View File

@@ -0,0 +1,190 @@
/* Copyright (C) 2000-2005 Constantin Kaplinsky. 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.
*/
#ifndef __RFB_PALETTE_H__
#define __RFB_PALETTE_H__
#include <assert.h>
#include <string.h>
#include <rdr/types.h>
namespace rfb {
class Palette {
public:
Palette() { clear(); }
~Palette() {}
int size() const { return numColours; }
void clear() { numColours = 0; memset(hash, 0, sizeof(hash)); }
inline bool insert(rdr::U32 colour, int numPixels);
inline unsigned char lookup(rdr::U32 colour) const;
inline rdr::U32 getColour(unsigned char index) const;
inline int getCount(unsigned char index) const;
protected:
inline unsigned char genHash(rdr::U32 colour) const;
protected:
int numColours;
struct PaletteListNode {
PaletteListNode *next;
unsigned char idx;
rdr::U32 colour;
};
struct PaletteEntry {
PaletteListNode *listNode;
int numPixels;
};
// This is the raw list of colours, allocated from 0 and up
PaletteListNode list[256];
// Hash table for quick lookup into the list above
PaletteListNode *hash[256];
// Occurances of each colour, where the 0:th entry is the most common.
// Indices also refer to this array.
PaletteEntry entry[256];
};
}
inline bool rfb::Palette::insert(rdr::U32 colour, int numPixels)
{
PaletteListNode* pnode;
PaletteListNode* prev_pnode;
unsigned char hash_key, idx;
hash_key = genHash(colour);
pnode = hash[hash_key];
prev_pnode = NULL;
// Do we already have an entry for this colour?
while (pnode != NULL) {
if (pnode->colour == colour) {
// Yup
idx = pnode->idx;
numPixels = entry[idx].numPixels + numPixels;
// The extra pixels might mean we have to adjust the sort list
while (idx > 0) {
if (entry[idx-1].numPixels >= numPixels)
break;
entry[idx] = entry[idx-1];
entry[idx].listNode->idx = idx;
idx--;
}
if (idx != pnode->idx) {
entry[idx].listNode = pnode;
pnode->idx = idx;
}
entry[idx].numPixels = numPixels;
return true;
}
prev_pnode = pnode;
pnode = pnode->next;
}
// Check if palette is full.
if (numColours == 256)
return false;
// Create a new colour entry
pnode = &list[numColours];
pnode->next = NULL;
pnode->idx = 0;
pnode->colour = colour;
// Add it to the hash table
if (prev_pnode != NULL)
prev_pnode->next = pnode;
else
hash[hash_key] = pnode;
// Move palette entries with lesser pixel counts.
idx = numColours;
while (idx > 0) {
if (entry[idx-1].numPixels >= numPixels)
break;
entry[idx] = entry[idx-1];
entry[idx].listNode->idx = idx;
idx--;
}
// And add it into the freed slot.
pnode->idx = idx;
entry[idx].listNode = pnode;
entry[idx].numPixels = numPixels;
numColours++;
return true;
}
inline unsigned char rfb::Palette::lookup(rdr::U32 colour) const
{
unsigned char hash_key;
PaletteListNode* pnode;
hash_key = genHash(colour);
pnode = hash[hash_key];
while (pnode != NULL) {
if (pnode->colour == colour)
return pnode->idx;
pnode = pnode->next;
}
// We are being fed a bad colour
assert(false);
return 0;
}
inline rdr::U32 rfb::Palette::getColour(unsigned char index) const
{
return entry[index].listNode->colour;
}
inline int rfb::Palette::getCount(unsigned char index) const
{
return entry[index].numPixels;
}
inline unsigned char rfb::Palette::genHash(rdr::U32 colour) const
{
unsigned char hash_key;
// djb2 hash function
hash_key = 5; // 5381 & 0xff
for (int i = 0; i < 32; i += 8)
hash_key = ((hash_key << 5) + hash_key) ^ (colour >> i);
return hash_key;
}
#endif

80
common/rfb/Password.cxx Normal file
View File

@@ -0,0 +1,80 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
//
// XXX not thread-safe, because d3des isn't - do we need to worry about this?
//
#include <string.h>
extern "C" {
#include <rfb/d3des.h>
}
#include <rdr/types.h>
#include <rdr/Exception.h>
#include <rfb/Password.h>
using namespace rfb;
static unsigned char d3desObfuscationKey[] = {23,82,107,6,35,78,88,7};
PlainPasswd::PlainPasswd() {}
PlainPasswd::PlainPasswd(char* pwd) : CharArray(pwd) {
}
PlainPasswd::PlainPasswd(int len) : CharArray(len) {
}
PlainPasswd::PlainPasswd(const ObfuscatedPasswd& obfPwd) : CharArray(9) {
if (obfPwd.length < 8)
throw rdr::Exception("bad obfuscated password length");
deskey(d3desObfuscationKey, DE1);
des((rdr::U8*)obfPwd.buf, (rdr::U8*)buf);
buf[8] = 0;
}
PlainPasswd::~PlainPasswd() {
replaceBuf(0);
}
void PlainPasswd::replaceBuf(char* b) {
if (buf)
memset(buf, 0, strlen(buf));
CharArray::replaceBuf(b);
}
ObfuscatedPasswd::ObfuscatedPasswd() : length(0) {
}
ObfuscatedPasswd::ObfuscatedPasswd(int len) : CharArray(len), length(len) {
}
ObfuscatedPasswd::ObfuscatedPasswd(const PlainPasswd& plainPwd) : CharArray(8), length(8) {
int l = strlen(plainPwd.buf), i;
for (i=0; i<8; i++)
buf[i] = i<l ? plainPwd.buf[i] : 0;
deskey(d3desObfuscationKey, EN0);
des((rdr::U8*)buf, (rdr::U8*)buf);
}
ObfuscatedPasswd::~ObfuscatedPasswd() {
if (buf)
memset(buf, 0, length);
}

47
common/rfb/Password.h Normal file
View File

@@ -0,0 +1,47 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
#ifndef __RFB_PASSWORD_H__
#define __RFB_PASSWORD_H__
#include <rfb/util.h>
namespace rfb {
class ObfuscatedPasswd;
class PlainPasswd : public CharArray {
public:
PlainPasswd();
PlainPasswd(char* pwd);
PlainPasswd(int len);
PlainPasswd(const ObfuscatedPasswd& obfPwd);
~PlainPasswd();
void replaceBuf(char* b);
};
class ObfuscatedPasswd : public CharArray {
public:
ObfuscatedPasswd();
ObfuscatedPasswd(int l);
ObfuscatedPasswd(const PlainPasswd& plainPwd);
~ObfuscatedPasswd();
int length;
};
}
#endif

26
common/rfb/Pixel.h Normal file
View File

@@ -0,0 +1,26 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
#ifndef __RFB_PIXEL_H__
#define __RFB_PIXEL_H__
#include <rdr/types.h>
namespace rfb {
typedef rdr::U32 Pixel; // must be big enough to hold any pixel value
}
#endif

373
common/rfb/PixelBuffer.cxx Normal file
View File

@@ -0,0 +1,373 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. 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.
*/
// -=- PixelBuffer.cxx
//
// The PixelBuffer class encapsulates the PixelFormat and dimensions
// of a block of pixel data.
#include <rfb/Exception.h>
#include <rfb/LogWriter.h>
#include <rfb/PixelBuffer.h>
using namespace rfb;
using namespace rdr;
static LogWriter vlog("PixelBuffer");
// -=- Generic pixel buffer class
PixelBuffer::PixelBuffer(const PixelFormat& pf, int w, int h)
: format(pf), width_(w), height_(h) {}
PixelBuffer::PixelBuffer() : width_(0), height_(0) {}
PixelBuffer::~PixelBuffer() {}
void
PixelBuffer::getImage(void* imageBuf, const Rect& r, int outStride) const
{
int inStride;
const U8* data;
int bytesPerPixel, inBytesPerRow, outBytesPerRow, bytesPerMemCpy;
U8* imageBufPos;
const U8* end;
if (!r.enclosed_by(getRect()))
throw rfb::Exception("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
r.width(), r.height(),
r.tl.x, r.tl.y, width_, height_);
data = getBuffer(r, &inStride);
bytesPerPixel = format.bpp/8;
inBytesPerRow = inStride * bytesPerPixel;
if (!outStride)
outStride = r.width();
outBytesPerRow = outStride * bytesPerPixel;
bytesPerMemCpy = r.width() * bytesPerPixel;
imageBufPos = (U8*)imageBuf;
end = data + (inBytesPerRow * r.height());
while (data < end) {
memcpy(imageBufPos, data, bytesPerMemCpy);
imageBufPos += outBytesPerRow;
data += inBytesPerRow;
}
}
void PixelBuffer::getImage(const PixelFormat& pf, void* imageBuf,
const Rect& r, int stride) const
{
const rdr::U8* srcBuffer;
int srcStride;
if (format.equal(pf)) {
getImage(imageBuf, r, stride);
return;
}
if (!r.enclosed_by(getRect()))
throw rfb::Exception("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
r.width(), r.height(),
r.tl.x, r.tl.y, width_, height_);
if (stride == 0)
stride = r.width();
srcBuffer = getBuffer(r, &srcStride);
pf.bufferFromBuffer((U8*)imageBuf, format, srcBuffer, r.width(), r.height(),
stride, srcStride);
}
// -=- Modifiable generic pixel buffer class
ModifiablePixelBuffer::ModifiablePixelBuffer(const PixelFormat& pf,
int w, int h)
: PixelBuffer(pf, w, h)
{
}
ModifiablePixelBuffer::ModifiablePixelBuffer()
{
}
ModifiablePixelBuffer::~ModifiablePixelBuffer()
{
}
void ModifiablePixelBuffer::fillRect(const Rect& r, const void* pix)
{
int stride;
U8 *buf;
int w, h, b;
if (!r.enclosed_by(getRect()))
throw rfb::Exception("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
r.width(), r.height(), r.tl.x, r.tl.y, width_, height_);
w = r.width();
h = r.height();
b = format.bpp/8;
if (h == 0)
return;
buf = getBufferRW(r, &stride);
if (b == 1) {
while (h--) {
memset(buf, *(const U8*)pix, w);
buf += stride * b;
}
} else {
U8 *start;
int w1;
start = buf;
w1 = w;
while (w1--) {
memcpy(buf, pix, b);
buf += b;
}
buf += (stride - w) * b;
h--;
while (h--) {
memcpy(buf, start, w * b);
buf += stride * b;
}
}
commitBufferRW(r);
}
void ModifiablePixelBuffer::imageRect(const Rect& r,
const void* pixels, int srcStride)
{
U8* dest;
int destStride;
int bytesPerPixel, bytesPerDestRow, bytesPerSrcRow, bytesPerFill;
const U8* src;
U8* end;
if (!r.enclosed_by(getRect()))
throw rfb::Exception("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
r.width(), r.height(),
r.tl.x, r.tl.y, width_, height_);
bytesPerPixel = getPF().bpp/8;
dest = getBufferRW(r, &destStride);
bytesPerDestRow = bytesPerPixel * destStride;
if (!srcStride)
srcStride = r.width();
bytesPerSrcRow = bytesPerPixel * srcStride;
bytesPerFill = bytesPerPixel * r.width();
src = (const U8*)pixels;
end = dest + (bytesPerDestRow * r.height());
while (dest < end) {
memcpy(dest, src, bytesPerFill);
dest += bytesPerDestRow;
src += bytesPerSrcRow;
}
commitBufferRW(r);
}
void ModifiablePixelBuffer::copyRect(const Rect &rect,
const Point &move_by_delta)
{
int srcStride, dstStride;
int bytesPerPixel;
const U8* srcData;
U8* dstData;
Rect drect, srect;
drect = rect;
if (!drect.enclosed_by(getRect()))
throw rfb::Exception("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
drect.width(), drect.height(),
drect.tl.x, drect.tl.y, width_, height_);
srect = drect.translate(move_by_delta.negate());
if (!srect.enclosed_by(getRect()))
throw rfb::Exception("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
srect.width(), srect.height(),
srect.tl.x, srect.tl.y, width_, height_);
bytesPerPixel = format.bpp/8;
srcData = getBuffer(srect, &srcStride);
dstData = getBufferRW(drect, &dstStride);
if (move_by_delta.y == 0) {
// Possible overlap. Be careful and use memmove().
int h = drect.height();
while (h--) {
memmove(dstData, srcData, drect.width() * bytesPerPixel);
dstData += dstStride * bytesPerPixel;
srcData += srcStride * bytesPerPixel;
}
} else if (move_by_delta.y < 0) {
// The data shifted upwards. Copy from top to bottom.
int h = drect.height();
while (h--) {
memcpy(dstData, srcData, drect.width() * bytesPerPixel);
dstData += dstStride * bytesPerPixel;
srcData += srcStride * bytesPerPixel;
}
} else {
// The data shifted downwards. Copy from bottom to top.
int h = drect.height();
dstData += (h-1) * dstStride * bytesPerPixel;
srcData += (h-1) * srcStride * bytesPerPixel;
while (h--) {
memcpy(dstData, srcData, drect.width() * bytesPerPixel);
dstData -= dstStride * bytesPerPixel;
srcData -= srcStride * bytesPerPixel;
}
}
commitBufferRW(drect);
}
void ModifiablePixelBuffer::fillRect(const PixelFormat& pf, const Rect &dest,
const void* pix)
{
rdr::U8 buf[4];
format.bufferFromBuffer(buf, pf, (const rdr::U8*)pix, 1);
fillRect(dest, buf);
}
void ModifiablePixelBuffer::imageRect(const PixelFormat& pf, const Rect &dest,
const void* pixels, int stride)
{
rdr::U8* dstBuffer;
int dstStride;
if (!dest.enclosed_by(getRect()))
throw rfb::Exception("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
dest.width(), dest.height(),
dest.tl.x, dest.tl.y, width_, height_);
if (stride == 0)
stride = dest.width();
dstBuffer = getBufferRW(dest, &dstStride);
format.bufferFromBuffer(dstBuffer, pf, (const rdr::U8*)pixels,
dest.width(), dest.height(),
dstStride, stride);
commitBufferRW(dest);
}
// -=- Simple pixel buffer with a continuous block of memory
FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h,
rdr::U8* data_, int stride_)
: ModifiablePixelBuffer(pf, w, h), data(data_), stride(stride_)
{
}
FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {}
FullFramePixelBuffer::~FullFramePixelBuffer() {}
rdr::U8* FullFramePixelBuffer::getBufferRW(const Rect& r, int* stride_)
{
if (!r.enclosed_by(getRect()))
throw rfb::Exception("Pixel buffer request %dx%d at %d,%d exceeds framebuffer %dx%d",
r.width(), r.height(),
r.tl.x, r.tl.y, width_, height_);
*stride_ = stride;
return &data[(r.tl.x + (r.tl.y * stride)) * (format.bpp/8)];
}
void FullFramePixelBuffer::commitBufferRW(const Rect& r)
{
}
const rdr::U8* FullFramePixelBuffer::getBuffer(const Rect& r, int* stride_) const
{
if (!r.enclosed_by(getRect()))
throw rfb::Exception("Pixel buffer request %dx%d at %d,%d exceeds framebuffer %dx%d",
r.width(), r.height(),
r.tl.x, r.tl.y, width_, height_);
*stride_ = stride;
return &data[(r.tl.x + (r.tl.y * stride)) * (format.bpp/8)];
}
// -=- Managed pixel buffer class
// Automatically allocates enough space for the specified format & area
ManagedPixelBuffer::ManagedPixelBuffer()
: datasize(0)
{
checkDataSize();
};
ManagedPixelBuffer::ManagedPixelBuffer(const PixelFormat& pf, int w, int h)
: FullFramePixelBuffer(pf, w, h, NULL, w), datasize(0)
{
checkDataSize();
};
ManagedPixelBuffer::~ManagedPixelBuffer() {
if (data) delete [] data;
};
void
ManagedPixelBuffer::setPF(const PixelFormat &pf) {
format = pf; checkDataSize();
};
void
ManagedPixelBuffer::setSize(int w, int h) {
width_ = w; height_ = h; stride = w; checkDataSize();
};
inline void
ManagedPixelBuffer::checkDataSize() {
unsigned long new_datasize = width_ * height_ * (format.bpp/8);
if (datasize < new_datasize) {
if (data) {
delete [] data;
datasize = 0; data = 0;
}
if (new_datasize) {
data = new U8[new_datasize];
if (!data)
throw Exception("rfb::ManagedPixelBuffer unable to allocate buffer");
datasize = new_datasize;
}
}
};

185
common/rfb/PixelBuffer.h Normal file
View File

@@ -0,0 +1,185 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. 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.
*/
// -=- PixelBuffer.h
//
// The PixelBuffer class encapsulates the PixelFormat and dimensions
// of a block of pixel data.
#ifndef __RFB_PIXEL_BUFFER_H__
#define __RFB_PIXEL_BUFFER_H__
#include <rfb/PixelFormat.h>
#include <rfb/Rect.h>
#include <rfb/Pixel.h>
#include <rfb/util.h>
namespace rfb {
class Region;
class PixelBuffer {
public:
PixelBuffer(const PixelFormat& pf, int width, int height);
virtual ~PixelBuffer();
///////////////////////////////////////////////
// Format / Layout
//
public:
// Get pixel format
const PixelFormat &getPF() const { return format; }
// Get width, height and number of pixels
int width() const { return width_; }
int height() const { return height_; }
int area() const { return width_ * height_; }
// Get rectangle encompassing this buffer
// Top-left of rectangle is either at (0,0), or the specified point.
Rect getRect() const { return Rect(0, 0, width_, height_); }
Rect getRect(const Point& pos) const {
return Rect(pos, pos.translate(Point(width_, height_)));
}
///////////////////////////////////////////////
// Access to pixel data
//
// Get a pointer into the buffer
// The pointer is to the top-left pixel of the specified Rect.
// The buffer stride (in pixels) is returned.
virtual const rdr::U8* getBuffer(const Rect& r, int* stride) const = 0;
// Get pixel data for a given part of the buffer
// Data is copied into the supplied buffer, with the specified
// stride. Try to avoid using this though as getBuffer() will in
// most cases avoid the extra memory copy.
void getImage(void* imageBuf, const Rect& r, int stride=0) const;
// Get pixel data in a given format
// Works just the same as getImage(), but guaranteed to be in a
// specific format.
void getImage(const PixelFormat& pf, void* imageBuf,
const Rect& r, int stride=0) const;
///////////////////////////////////////////////
// Framebuffer update methods
//
// Ensure that the specified rectangle of buffer is up to date.
// Overridden by derived classes implementing framebuffer access
// to copy the required display data into place.
virtual void grabRegion(const Region& __unused_attr region) {}
protected:
PixelBuffer();
PixelFormat format;
int width_, height_;
};
// ModifiablePixelBuffer
class ModifiablePixelBuffer : public PixelBuffer {
public:
ModifiablePixelBuffer(const PixelFormat& pf, int width, int height);
virtual ~ModifiablePixelBuffer();
///////////////////////////////////////////////
// Access to pixel data
//
// Get a writeable pointer into the buffer
// Like getBuffer(), the pointer is to the top-left pixel of the
// specified Rect and the stride in pixels is returned.
virtual rdr::U8* getBufferRW(const Rect& r, int* stride) = 0;
// Commit the modified contents
// Ensures that the changes to the specified Rect is properly
// stored away and any temporary buffers are freed. The Rect given
// here needs to match the Rect given to the earlier call to
// getBufferRW().
virtual void commitBufferRW(const Rect& r) = 0;
///////////////////////////////////////////////
// Basic rendering operations
// These operations DO NOT clip to the pixelbuffer area, or trap overruns.
// Fill a rectangle
void fillRect(const Rect &dest, const void* pix);
// Copy pixel data to the buffer
void imageRect(const Rect &dest, const void* pixels, int stride=0);
// Copy pixel data from one PixelBuffer location to another
void copyRect(const Rect &dest, const Point& move_by_delta);
// Render in a specific format
// Does the exact same thing as the above methods, but the given
// pixel values are defined by the given PixelFormat.
void fillRect(const PixelFormat& pf, const Rect &dest, const void* pix);
void imageRect(const PixelFormat& pf, const Rect &dest,
const void* pixels, int stride=0);
protected:
ModifiablePixelBuffer();
};
// FullFramePixelBuffer
class FullFramePixelBuffer : public ModifiablePixelBuffer {
public:
FullFramePixelBuffer(const PixelFormat& pf, int width, int height,
rdr::U8* data_, int stride);
virtual ~FullFramePixelBuffer();
public:
virtual const rdr::U8* getBuffer(const Rect& r, int* stride) const;
virtual rdr::U8* getBufferRW(const Rect& r, int* stride);
virtual void commitBufferRW(const Rect& r);
protected:
FullFramePixelBuffer();
rdr::U8* data;
int stride;
};
// -=- Managed pixel buffer class
// Automatically allocates enough space for the specified format & area
class ManagedPixelBuffer : public FullFramePixelBuffer {
public:
ManagedPixelBuffer();
ManagedPixelBuffer(const PixelFormat& pf, int width, int height);
virtual ~ManagedPixelBuffer();
// Manage the pixel buffer layout
virtual void setPF(const PixelFormat &pf);
virtual void setSize(int w, int h);
// Return the total number of bytes of pixel data in the buffer
int dataLen() const { return width_ * height_ * (format.bpp/8); }
protected:
unsigned long datasize;
void checkDataSize();
};
};
#endif // __RFB_PIXEL_BUFFER_H__

731
common/rfb/PixelFormat.cxx Normal file
View File

@@ -0,0 +1,731 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
* Copyright 2009-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 <assert.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <rdr/InStream.h>
#include <rdr/OutStream.h>
#include <rfb/Exception.h>
#include <rfb/PixelFormat.h>
#include <rfb/util.h>
#ifdef _WIN32
#define strcasecmp _stricmp
#endif
using namespace rfb;
rdr::U8 PixelFormat::upconvTable[256*8];
rdr::U8 PixelFormat::downconvTable[256*8];
class PixelFormat::Init {
public:
Init();
};
PixelFormat::Init PixelFormat::_init;
PixelFormat::Init::Init()
{
int bits;
// Shifting bits is almost perfect, but not quite. And
// a lookup table is still quicker when there is a large
// difference between the source and destination depth.
for (bits = 1;bits <= 8;bits++) {
int i, maxVal;
rdr::U8 *subUpTable;
rdr::U8 *subDownTable;
maxVal = (1 << bits) - 1;
subUpTable = &upconvTable[(bits-1)*256];
subDownTable = &downconvTable[(bits-1)*256];
for (i = 0;i <= maxVal;i++)
subUpTable[i] = i * 255 / maxVal;
// Duplicate the up table so that we don't have to care about
// the upper bits when doing a lookup
for (;i < 256;i += maxVal+1)
memcpy(&subUpTable[i], &subUpTable[0], maxVal+1);
for (i = 0;i <= 255;i++)
subDownTable[i] = (i * maxVal + 128) / 255;
}
}
PixelFormat::PixelFormat(int b, int d, bool e, bool t,
int rm, int gm, int bm, int rs, int gs, int bs)
: bpp(b), depth(d), trueColour(t), bigEndian(e),
redMax(rm), greenMax(gm), blueMax(bm),
redShift(rs), greenShift(gs), blueShift(bs)
{
assert(isSane());
updateState();
}
PixelFormat::PixelFormat()
: bpp(8), depth(8), trueColour(true), bigEndian(false),
redMax(7), greenMax(7), blueMax(3),
redShift(0), greenShift(3), blueShift(6)
{
updateState();
}
bool PixelFormat::equal(const PixelFormat& other) const
{
if (bpp != other.bpp || depth != other.depth)
return false;
if (redMax != other.redMax)
return false;
if (greenMax != other.greenMax)
return false;
if (blueMax != other.blueMax)
return false;
// Endianness requires more care to determine compatibility
if (bigEndian == other.bigEndian || bpp == 8) {
if (redShift != other.redShift)
return false;
if (greenShift != other.greenShift)
return false;
if (blueShift != other.blueShift)
return false;
} else {
// Has to be the same byte for each channel
if (redShift/8 != (3 - other.redShift/8))
return false;
if (greenShift/8 != (3 - other.greenShift/8))
return false;
if (blueShift/8 != (3 - other.blueShift/8))
return false;
// And the same bit offset within the byte
if (redShift%8 != other.redShift%8)
return false;
if (greenShift%8 != other.greenShift%8)
return false;
if (blueShift%8 != other.blueShift%8)
return false;
// And not cross a byte boundary
if (redShift/8 != (redShift + redBits - 1)/8)
return false;
if (greenShift/8 != (greenShift + greenBits - 1)/8)
return false;
if (blueShift/8 != (blueShift + blueBits - 1)/8)
return false;
}
return true;
}
void PixelFormat::read(rdr::InStream* is)
{
bpp = is->readU8();
depth = is->readU8();
bigEndian = is->readU8();
trueColour = is->readU8();
redMax = is->readU16();
greenMax = is->readU16();
blueMax = is->readU16();
redShift = is->readU8();
greenShift = is->readU8();
blueShift = is->readU8();
is->skip(3);
// We have no real support for colour maps. If the client
// wants one, then we force a 8-bit true colour format and
// pretend it's a colour map.
if (!trueColour) {
redMax = 7;
greenMax = 7;
blueMax = 3;
redShift = 0;
greenShift = 3;
blueShift = 6;
}
if (!isSane())
throw Exception("invalid pixel format");
updateState();
}
void PixelFormat::write(rdr::OutStream* os) const
{
os->writeU8(bpp);
os->writeU8(depth);
os->writeU8(bigEndian);
os->writeU8(trueColour);
os->writeU16(redMax);
os->writeU16(greenMax);
os->writeU16(blueMax);
os->writeU8(redShift);
os->writeU8(greenShift);
os->writeU8(blueShift);
os->pad(3);
}
bool PixelFormat::is888(void) const
{
if (!trueColour)
return false;
if (bpp != 32)
return false;
if (depth != 24)
return false;
if (redMax != 255)
return false;
if (greenMax != 255)
return false;
if (blueMax != 255)
return false;
return true;
}
bool PixelFormat::isBigEndian(void) const
{
return bigEndian;
}
bool PixelFormat::isLittleEndian(void) const
{
return ! bigEndian;
}
void PixelFormat::bufferFromRGB(rdr::U8 *dst, const rdr::U8* src, int pixels) const
{
bufferFromRGB(dst, src, pixels, pixels, 1);
}
void PixelFormat::bufferFromRGB(rdr::U8 *dst, const rdr::U8* src,
int w, int stride, int h) const
{
if (is888()) {
// Optimised common case
rdr::U8 *r, *g, *b, *x;
if (bigEndian) {
r = dst + (24 - redShift)/8;
g = dst + (24 - greenShift)/8;
b = dst + (24 - blueShift)/8;
x = dst + (24 - (48 - redShift - greenShift - blueShift))/8;
} else {
r = dst + redShift/8;
g = dst + greenShift/8;
b = dst + blueShift/8;
x = dst + (48 - redShift - greenShift - blueShift)/8;
}
int dstPad = (stride - w) * 4;
while (h--) {
int w_ = w;
while (w_--) {
*r = *(src++);
*g = *(src++);
*b = *(src++);
*x = 0;
r += 4;
g += 4;
b += 4;
x += 4;
}
r += dstPad;
g += dstPad;
b += dstPad;
x += dstPad;
}
} else {
// Generic code
int dstPad = (stride - w) * bpp/8;
while (h--) {
int w_ = w;
while (w_--) {
Pixel p;
rdr::U8 r, g, b;
r = *(src++);
g = *(src++);
b = *(src++);
p = pixelFromRGB(r, g, b);
bufferFromPixel(dst, p);
dst += bpp/8;
}
dst += dstPad;
}
}
}
void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src, int pixels) const
{
rgbFromBuffer(dst, src, pixels, pixels, 1);
}
void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src,
int w, int stride, int h) const
{
if (is888()) {
// Optimised common case
const rdr::U8 *r, *g, *b;
if (bigEndian) {
r = src + (24 - redShift)/8;
g = src + (24 - greenShift)/8;
b = src + (24 - blueShift)/8;
} else {
r = src + redShift/8;
g = src + greenShift/8;
b = src + blueShift/8;
}
int srcPad = (stride - w) * 4;
while (h--) {
int w_ = w;
while (w_--) {
*(dst++) = *r;
*(dst++) = *g;
*(dst++) = *b;
r += 4;
g += 4;
b += 4;
}
r += srcPad;
g += srcPad;
b += srcPad;
}
} else {
// Generic code
int srcPad = (stride - w) * bpp/8;
while (h--) {
int w_ = w;
while (w_--) {
Pixel p;
rdr::U8 r, g, b;
p = pixelFromBuffer(src);
rgbFromPixel(p, &r, &g, &b);
*(dst++) = r;
*(dst++) = g;
*(dst++) = b;
src += bpp/8;
}
src += srcPad;
}
}
}
Pixel PixelFormat::pixelFromPixel(const PixelFormat &srcPF, Pixel src) const
{
rdr::U16 r, g, b;
srcPF.rgbFromPixel(src, &r, &g, &b);
return pixelFromRGB(r, g, b);
}
void PixelFormat::bufferFromBuffer(rdr::U8* dst, const PixelFormat &srcPF,
const rdr::U8* src, int pixels) const
{
bufferFromBuffer(dst, srcPF, src, pixels, 1, pixels, pixels);
}
#define IS_ALIGNED(v, a) (((intptr_t)v & (a-1)) == 0)
void PixelFormat::bufferFromBuffer(rdr::U8* dst, const PixelFormat &srcPF,
const rdr::U8* src, int w, int h,
int dstStride, int srcStride) const
{
if (equal(srcPF)) {
// Trivial case
while (h--) {
memcpy(dst, src, w * bpp/8);
dst += dstStride * bpp/8;
src += srcStride * srcPF.bpp/8;
}
} else if (is888() && srcPF.is888()) {
// Optimised common case A: byte shuffling (e.g. endian conversion)
rdr::U8 *d[4], *s[4];
int dstPad, srcPad;
if (bigEndian) {
s[0] = dst + (24 - redShift)/8;
s[1] = dst + (24 - greenShift)/8;
s[2] = dst + (24 - blueShift)/8;
s[3] = dst + (24 - (48 - redShift - greenShift - blueShift))/8;
} else {
s[0] = dst + redShift/8;
s[1] = dst + greenShift/8;
s[2] = dst + blueShift/8;
s[3] = dst + (48 - redShift - greenShift - blueShift)/8;
}
if (srcPF.bigEndian) {
d[(24 - srcPF.redShift)/8] = s[0];
d[(24 - srcPF.greenShift)/8] = s[1];
d[(24 - srcPF.blueShift)/8] = s[2];
d[(24 - (48 - srcPF.redShift - srcPF.greenShift - srcPF.blueShift))/8] = s[3];
} else {
d[srcPF.redShift/8] = s[0];
d[srcPF.greenShift/8] = s[1];
d[srcPF.blueShift/8] = s[2];
d[(48 - srcPF.redShift - srcPF.greenShift - srcPF.blueShift)/8] = s[3];
}
dstPad = (dstStride - w) * 4;
srcPad = (srcStride - w) * 4;
while (h--) {
int w_ = w;
while (w_--) {
*d[0] = *(src++);
*d[1] = *(src++);
*d[2] = *(src++);
*d[3] = *(src++);
d[0] += 4;
d[1] += 4;
d[2] += 4;
d[3] += 4;
}
d[0] += dstPad;
d[1] += dstPad;
d[2] += dstPad;
d[3] += dstPad;
src += srcPad;
}
} else if (IS_ALIGNED(dst, bpp/8) && srcPF.is888()) {
// Optimised common case B: 888 source
switch (bpp) {
case 8:
directBufferFromBufferFrom888((rdr::U8*)dst, srcPF, src,
w, h, dstStride, srcStride);
break;
case 16:
directBufferFromBufferFrom888((rdr::U16*)dst, srcPF, src,
w, h, dstStride, srcStride);
break;
case 32:
directBufferFromBufferFrom888((rdr::U32*)dst, srcPF, src,
w, h, dstStride, srcStride);
break;
}
} else if (IS_ALIGNED(src, srcPF.bpp/8) && is888()) {
// Optimised common case C: 888 destination
switch (srcPF.bpp) {
case 8:
directBufferFromBufferTo888(dst, srcPF, (rdr::U8*)src,
w, h, dstStride, srcStride);
break;
case 16:
directBufferFromBufferTo888(dst, srcPF, (rdr::U16*)src,
w, h, dstStride, srcStride);
break;
case 32:
directBufferFromBufferTo888(dst, srcPF, (rdr::U32*)src,
w, h, dstStride, srcStride);
break;
}
} else {
// Generic code
int dstPad = (dstStride - w) * bpp/8;
int srcPad = (srcStride - w) * srcPF.bpp/8;
while (h--) {
int w_ = w;
while (w_--) {
Pixel p;
rdr::U8 r, g, b;
p = srcPF.pixelFromBuffer(src);
srcPF.rgbFromPixel(p, &r, &g, &b);
p = pixelFromRGB(r, g, b);
bufferFromPixel(dst, p);
dst += bpp/8;
src += srcPF.bpp/8;
}
dst += dstPad;
src += srcPad;
}
}
}
void PixelFormat::print(char* str, int len) const
{
// Unfortunately snprintf is not widely available so we build the string up
// using strncat - not pretty, but should be safe against buffer overruns.
char num[20];
if (len < 1) return;
str[0] = 0;
strncat(str, "depth ", len-1-strlen(str));
sprintf(num,"%d",depth);
strncat(str, num, len-1-strlen(str));
strncat(str, " (", len-1-strlen(str));
sprintf(num,"%d",bpp);
strncat(str, num, len-1-strlen(str));
strncat(str, "bpp)", len-1-strlen(str));
if (bpp != 8) {
if (bigEndian)
strncat(str, " big-endian", len-1-strlen(str));
else
strncat(str, " little-endian", len-1-strlen(str));
}
if (!trueColour) {
strncat(str, " color-map", len-1-strlen(str));
return;
}
if (blueShift == 0 && greenShift > blueShift && redShift > greenShift &&
blueMax == (1 << greenShift) - 1 &&
greenMax == (1 << (redShift-greenShift)) - 1 &&
redMax == (1 << (depth-redShift)) - 1)
{
strncat(str, " rgb", len-1-strlen(str));
sprintf(num,"%d",depth-redShift);
strncat(str, num, len-1-strlen(str));
sprintf(num,"%d",redShift-greenShift);
strncat(str, num, len-1-strlen(str));
sprintf(num,"%d",greenShift);
strncat(str, num, len-1-strlen(str));
return;
}
if (redShift == 0 && greenShift > redShift && blueShift > greenShift &&
redMax == (1 << greenShift) - 1 &&
greenMax == (1 << (blueShift-greenShift)) - 1 &&
blueMax == (1 << (depth-blueShift)) - 1)
{
strncat(str, " bgr", len-1-strlen(str));
sprintf(num,"%d",depth-blueShift);
strncat(str, num, len-1-strlen(str));
sprintf(num,"%d",blueShift-greenShift);
strncat(str, num, len-1-strlen(str));
sprintf(num,"%d",greenShift);
strncat(str, num, len-1-strlen(str));
return;
}
strncat(str, " rgb max ", len-1-strlen(str));
sprintf(num,"%d,",redMax);
strncat(str, num, len-1-strlen(str));
sprintf(num,"%d,",greenMax);
strncat(str, num, len-1-strlen(str));
sprintf(num,"%d",blueMax);
strncat(str, num, len-1-strlen(str));
strncat(str, " shift ", len-1-strlen(str));
sprintf(num,"%d,",redShift);
strncat(str, num, len-1-strlen(str));
sprintf(num,"%d,",greenShift);
strncat(str, num, len-1-strlen(str));
sprintf(num,"%d",blueShift);
strncat(str, num, len-1-strlen(str));
}
bool PixelFormat::parse(const char* str)
{
char rgbbgr[4];
int bits1, bits2, bits3;
if (sscanf(str, "%3s%1d%1d%1d", rgbbgr, &bits1, &bits2, &bits3) < 4)
return false;
depth = bits1 + bits2 + bits3;
bpp = depth <= 8 ? 8 : ((depth <= 16) ? 16 : 32);
trueColour = true;
rdr::U32 endianTest = 1;
bigEndian = (*(rdr::U8*)&endianTest == 0);
greenShift = bits3;
greenMax = (1 << bits2) - 1;
if (strcasecmp(rgbbgr, "bgr") == 0) {
redShift = 0;
redMax = (1 << bits3) - 1;
blueShift = bits3 + bits2;
blueMax = (1 << bits1) - 1;
} else if (strcasecmp(rgbbgr, "rgb") == 0) {
blueShift = 0;
blueMax = (1 << bits3) - 1;
redShift = bits3 + bits2;
redMax = (1 << bits1) - 1;
} else {
return false;
}
assert(isSane());
updateState();
return true;
}
static int bits(rdr::U16 value)
{
int bits;
bits = 16;
if (!(value & 0xff00)) {
bits -= 8;
value <<= 8;
}
if (!(value & 0xf000)) {
bits -= 4;
value <<= 4;
}
if (!(value & 0xc000)) {
bits -= 2;
value <<= 2;
}
if (!(value & 0x8000)) {
bits -= 1;
value <<= 1;
}
return bits;
}
void PixelFormat::updateState(void)
{
int endianTest = 1;
redBits = bits(redMax);
greenBits = bits(greenMax);
blueBits = bits(blueMax);
maxBits = redBits;
if (greenBits > maxBits)
maxBits = greenBits;
if (blueBits > maxBits)
maxBits = blueBits;
minBits = redBits;
if (greenBits < minBits)
minBits = greenBits;
if (blueBits < minBits)
minBits = blueBits;
if (((*(char*)&endianTest) == 0) != bigEndian)
endianMismatch = true;
else
endianMismatch = false;
}
bool PixelFormat::isSane(void)
{
int totalBits;
if ((bpp != 8) && (bpp != 16) && (bpp != 32))
return false;
if (depth > bpp)
return false;
if (!trueColour && (depth != 8))
return false;
if ((redMax & (redMax + 1)) != 0)
return false;
if ((greenMax & (greenMax + 1)) != 0)
return false;
if ((blueMax & (blueMax + 1)) != 0)
return false;
/*
* We don't allow individual channels > 8 bits in order to keep our
* conversions simple.
*/
if (redMax >= (1 << 8))
return false;
if (greenMax >= (1 << 8))
return false;
if (blueMax >= (1 << 8))
return false;
totalBits = bits(redMax) + bits(greenMax) + bits(blueMax);
if (totalBits > bpp)
return false;
if (((redMax << redShift) & (greenMax << greenShift)) != 0)
return false;
if (((redMax << redShift) & (blueMax << blueShift)) != 0)
return false;
if (((greenMax << greenShift) & (blueMax << blueShift)) != 0)
return false;
return true;
}
// Preprocessor generated, optimised methods
#define INBPP 8
#define OUTBPP 8
#include "PixelFormatBPP.cxx"
#undef OUTBPP
#define OUTBPP 16
#include "PixelFormatBPP.cxx"
#undef OUTBPP
#define OUTBPP 32
#include "PixelFormatBPP.cxx"
#undef OUTBPP
#undef INBPP
#define INBPP 16
#define OUTBPP 8
#include "PixelFormatBPP.cxx"
#undef OUTBPP
#define OUTBPP 16
#include "PixelFormatBPP.cxx"
#undef OUTBPP
#define OUTBPP 32
#include "PixelFormatBPP.cxx"
#undef OUTBPP
#undef INBPP
#define INBPP 32
#define OUTBPP 8
#include "PixelFormatBPP.cxx"
#undef OUTBPP
#define OUTBPP 16
#include "PixelFormatBPP.cxx"
#undef OUTBPP
#define OUTBPP 32
#include "PixelFormatBPP.cxx"
#undef OUTBPP
#undef INBPP

156
common/rfb/PixelFormat.h Normal file
View File

@@ -0,0 +1,156 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
* Copyright 2009-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.
*/
//
// PixelFormat - structure to represent a pixel format. Also has useful
// methods for reading & writing to streams, etc. Conversion to and from
// other formats are also handled by this class. We have three different
// representations that we refer to:
//
// a) Pixels - Unsigned native integers in the format specified by this
// PixelFormat object.
// b) Buffer - Same thing as pixels, but in the appropriate byte stream
// format. This involves endian conversion and padding.
// c) RGB - A byte stream of 8 bit red, green and blue elements, in that
// order.
//
#ifndef __RFB_PIXELFORMAT_H__
#define __RFB_PIXELFORMAT_H__
#include <rfb/Pixel.h>
namespace rdr { class InStream; class OutStream; }
namespace rfb {
class PixelFormat {
public:
PixelFormat(int b, int d, bool e, bool t,
int rm, int gm, int bm, int rs, int gs, int bs);
PixelFormat();
// Checks if the formats have identical buffer representation.
// They might still have different pixel representation, endianness
// or true colour state.
bool equal(const PixelFormat& other) const;
void read(rdr::InStream* is);
void write(rdr::OutStream* os) const;
bool is888(void) const;
bool isBigEndian(void) const;
bool isLittleEndian(void) const;
inline Pixel pixelFromBuffer(const rdr::U8* buffer) const;
inline void bufferFromPixel(rdr::U8* buffer, Pixel pixel) const;
inline Pixel pixelFromRGB(rdr::U16 red, rdr::U16 green, rdr::U16 blue) const;
inline Pixel pixelFromRGB(rdr::U8 red, rdr::U8 green, rdr::U8 blue) const;
void bufferFromRGB(rdr::U8 *dst, const rdr::U8* src, int pixels) const;
void bufferFromRGB(rdr::U8 *dst, const rdr::U8* src,
int w, int stride, int h) const;
inline void rgbFromPixel(Pixel pix, rdr::U16 *r, rdr::U16 *g, rdr::U16 *b) const;
inline void rgbFromPixel(Pixel pix, rdr::U8 *r, rdr::U8 *g, rdr::U8 *b) const;
void rgbFromBuffer(rdr::U8* dst, const rdr::U8* src, int pixels) const;
void rgbFromBuffer(rdr::U8* dst, const rdr::U8* src,
int w, int stride, int h) const;
Pixel pixelFromPixel(const PixelFormat &srcPF, Pixel src) const;
void bufferFromBuffer(rdr::U8* dst, const PixelFormat &srcPF,
const rdr::U8* src, int pixels) const;
void bufferFromBuffer(rdr::U8* dst, const PixelFormat &srcPF,
const rdr::U8* src, int w, int h,
int dstStride, int srcStride) const;
void print(char* str, int len) const;
bool parse(const char* str);
protected:
void updateState(void);
bool isSane(void);
private:
// Preprocessor generated, optimised methods
void directBufferFromBufferFrom888(rdr::U8* dst, const PixelFormat &srcPF,
const rdr::U8* src, int w, int h,
int dstStride, int srcStride) const;
void directBufferFromBufferFrom888(rdr::U16* dst, const PixelFormat &srcPF,
const rdr::U8* src, int w, int h,
int dstStride, int srcStride) const;
void directBufferFromBufferFrom888(rdr::U32* dst, const PixelFormat &srcPF,
const rdr::U8* src, int w, int h,
int dstStride, int srcStride) const;
void directBufferFromBufferTo888(rdr::U8* dst, const PixelFormat &srcPF,
const rdr::U8* src, int w, int h,
int dstStride, int srcStride) const;
void directBufferFromBufferTo888(rdr::U8* dst, const PixelFormat &srcPF,
const rdr::U16* src, int w, int h,
int dstStride, int srcStride) const;
void directBufferFromBufferTo888(rdr::U8* dst, const PixelFormat &srcPF,
const rdr::U32* src, int w, int h,
int dstStride, int srcStride) const;
public:
int bpp;
int depth;
// This only tracks if the client thinks it is in colour map mode.
// In practice we are always in true colour mode.
bool trueColour;
protected:
bool bigEndian;
int redMax;
int greenMax;
int blueMax;
int redShift;
int greenShift;
int blueShift;
protected:
/* Pre-computed values to keep algorithms simple */
int redBits, greenBits, blueBits;
int maxBits, minBits;
bool endianMismatch;
static rdr::U8 upconvTable[256*8];
static rdr::U8 downconvTable[256*8];
class Init;
friend class Init;
static Init _init;
/* Only for testing this class */
friend void makePixel(const rfb::PixelFormat &, rdr::U8 *);
friend bool verifyPixel(const rfb::PixelFormat &,
const rfb::PixelFormat &,
const rdr::U8 *);
};
}
#include <rfb/PixelFormat.inl>
#endif

135
common/rfb/PixelFormat.inl Normal file
View File

@@ -0,0 +1,135 @@
/* Copyright 2009-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.
*/
namespace rfb {
inline Pixel PixelFormat::pixelFromBuffer(const rdr::U8* buffer) const
{
Pixel p;
p = 0;
if (bigEndian) {
switch (bpp) {
case 32:
p |= ((Pixel)*(buffer++)) << 24;
p |= ((Pixel)*(buffer++)) << 16;
case 16:
p |= ((Pixel)*(buffer++)) << 8;
case 8:
p |= *buffer;
}
} else {
p |= buffer[0];
if (bpp >= 16) {
p |= ((Pixel)buffer[1]) << 8;
if (bpp == 32) {
p |= ((Pixel)buffer[2]) << 16;
p |= ((Pixel)buffer[3]) << 24;
}
}
}
return p;
}
inline void PixelFormat::bufferFromPixel(rdr::U8* buffer, Pixel p) const
{
if (bigEndian) {
switch (bpp) {
case 32:
*(buffer++) = (p >> 24) & 0xff;
*(buffer++) = (p >> 16) & 0xff;
case 16:
*(buffer++) = (p >> 8) & 0xff;
case 8:
*(buffer++) = (p >> 0) & 0xff;
}
} else {
buffer[0] = (p >> 0) & 0xff;
if (bpp >= 16) {
buffer[1] = (p >> 8) & 0xff;
if (bpp == 32) {
buffer[2] = (p >> 16) & 0xff;
buffer[3] = (p >> 24) & 0xff;
}
}
}
}
inline Pixel PixelFormat::pixelFromRGB(rdr::U16 red, rdr::U16 green, rdr::U16 blue) const
{
Pixel p;
p = (Pixel)downconvTable[(redBits-1)*256 + (red >> 8)] << redShift;
p |= (Pixel)downconvTable[(greenBits-1)*256 + (green >> 8)] << greenShift;
p |= (Pixel)downconvTable[(blueBits-1)*256 + (blue >> 8)] << blueShift;
return p;
}
inline Pixel PixelFormat::pixelFromRGB(rdr::U8 red, rdr::U8 green, rdr::U8 blue) const
{
Pixel p;
p = (Pixel)downconvTable[(redBits-1)*256 + red] << redShift;
p |= (Pixel)downconvTable[(greenBits-1)*256 + green] << greenShift;
p |= (Pixel)downconvTable[(blueBits-1)*256 + blue] << blueShift;
return p;
}
inline void PixelFormat::rgbFromPixel(Pixel p, rdr::U16 *r, rdr::U16 *g, rdr::U16 *b) const
{
rdr::U8 _r, _g, _b;
_r = p >> redShift;
_g = p >> greenShift;
_b = p >> blueShift;
_r = upconvTable[(redBits-1)*256 + _r];
_g = upconvTable[(greenBits-1)*256 + _g];
_b = upconvTable[(blueBits-1)*256 + _b];
*r = _r << 8 | _r;
*g = _g << 8 | _g;
*b = _b << 8 | _b;
}
inline void PixelFormat::rgbFromPixel(Pixel p, rdr::U8 *r, rdr::U8 *g, rdr::U8 *b) const
{
rdr::U8 _r, _g, _b;
_r = p >> redShift;
_g = p >> greenShift;
_b = p >> blueShift;
*r = upconvTable[(redBits-1)*256 + _r];
*g = upconvTable[(greenBits-1)*256 + _g];
*b = upconvTable[(blueBits-1)*256 + _b];
}
}

View File

@@ -0,0 +1,155 @@
/* 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.
*/
#define CONCAT2(a,b) a##b
#define CONCAT2E(a,b) CONCAT2(a,b)
#define UIN CONCAT2E(U,INBPP)
#define UOUT CONCAT2E(U,OUTBPP)
#define SWAP16(n) ((((n) & 0xff) << 8) | (((n) >> 8) & 0xff))
#define SWAP32(n) (((n) >> 24) | (((n) & 0x00ff0000) >> 8) | \
(((n) & 0x0000ff00) << 8) | ((n) << 24))
#define SWAPIN CONCAT2E(SWAP,INBPP)
#define SWAPOUT CONCAT2E(SWAP,OUTBPP)
#if INBPP == 32
void PixelFormat::directBufferFromBufferFrom888(rdr::UOUT* dst,
const PixelFormat &srcPF,
const rdr::U8* src,
int w, int h,
int dstStride,
int srcStride) const
{
const rdr::U8 *r, *g, *b;
int dstPad, srcPad;
const rdr::U8 *redDownTable, *greenDownTable, *blueDownTable;
redDownTable = &downconvTable[(redBits-1)*256];
greenDownTable = &downconvTable[(greenBits-1)*256];
blueDownTable = &downconvTable[(blueBits-1)*256];
if (srcPF.bigEndian) {
r = src + (24 - srcPF.redShift)/8;
g = src + (24 - srcPF.greenShift)/8;
b = src + (24 - srcPF.blueShift)/8;
} else {
r = src + srcPF.redShift/8;
g = src + srcPF.greenShift/8;
b = src + srcPF.blueShift/8;
}
dstPad = (dstStride - w);
srcPad = (srcStride - w) * 4;
while (h--) {
int w_ = w;
while (w_--) {
rdr::UOUT d;
d = redDownTable[*r] << redShift;
d |= greenDownTable[*g] << greenShift;
d |= blueDownTable[*b] << blueShift;
#if OUTBPP != 8
if (endianMismatch)
d = SWAPOUT(d);
#endif
*dst = d;
dst++;
r += 4;
g += 4;
b += 4;
}
dst += dstPad;
r += srcPad;
g += srcPad;
b += srcPad;
}
}
#endif /* INBPP == 32 */
#if OUTBPP == 32
void PixelFormat::directBufferFromBufferTo888(rdr::U8* dst,
const PixelFormat &srcPF,
const rdr::UIN* src,
int w, int h,
int dstStride,
int srcStride) const
{
rdr::U8 *r, *g, *b, *x;
int dstPad, srcPad;
const rdr::U8 *redUpTable, *greenUpTable, *blueUpTable;
redUpTable = &upconvTable[(srcPF.redBits-1)*256];
greenUpTable = &upconvTable[(srcPF.greenBits-1)*256];
blueUpTable = &upconvTable[(srcPF.blueBits-1)*256];
if (bigEndian) {
r = dst + (24 - redShift)/8;
g = dst + (24 - greenShift)/8;
b = dst + (24 - blueShift)/8;
x = dst + (24 - (48 - redShift - greenShift - blueShift))/8;
} else {
r = dst + redShift/8;
g = dst + greenShift/8;
b = dst + blueShift/8;
x = dst + (48 - redShift - greenShift - blueShift)/8;
}
dstPad = (dstStride - w) * 4;
srcPad = (srcStride - w);
while (h--) {
int w_ = w;
while (w_--) {
rdr::UIN s;
s = *src;
#if INBPP != 8
if (srcPF.endianMismatch)
s = SWAPIN(s);
#endif
*r = redUpTable[(s >> srcPF.redShift) & 0xff];
*g = greenUpTable[(s >> srcPF.greenShift) & 0xff];
*b = blueUpTable[(s >> srcPF.blueShift) & 0xff];
*x = 0;
r += 4;
g += 4;
b += 4;
x += 4;
src++;
}
r += dstPad;
g += dstPad;
b += dstPad;
x += dstPad;
src += srcPad;
}
}
#endif /* OUTBPP == 32 */

69
common/rfb/RREDecoder.cxx Normal file
View File

@@ -0,0 +1,69 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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 <rdr/InStream.h>
#include <rdr/MemInStream.h>
#include <rdr/OutStream.h>
#include <rfb/ConnParams.h>
#include <rfb/PixelBuffer.h>
#include <rfb/RREDecoder.h>
using namespace rfb;
#define BPP 8
#include <rfb/rreDecode.h>
#undef BPP
#define BPP 16
#include <rfb/rreDecode.h>
#undef BPP
#define BPP 32
#include <rfb/rreDecode.h>
#undef BPP
RREDecoder::RREDecoder() : Decoder(DecoderPlain)
{
}
RREDecoder::~RREDecoder()
{
}
void RREDecoder::readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, 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));
}
void RREDecoder::decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
ModifiablePixelBuffer* pb)
{
rdr::MemInStream is(buffer, buflen);
const PixelFormat& pf = cp.pf();
switch (pf.bpp) {
case 8: rreDecode8 (r, &is, pf, pb); break;
case 16: rreDecode16(r, &is, pf, pb); break;
case 32: rreDecode32(r, &is, pf, pb); break;
}
}

36
common/rfb/RREDecoder.h Normal file
View File

@@ -0,0 +1,36 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
#ifndef __RFB_RREDECODER_H__
#define __RFB_RREDECODER_H__
#include <rfb/Decoder.h>
namespace rfb {
class RREDecoder : public Decoder {
public:
RREDecoder();
virtual ~RREDecoder();
virtual void readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os);
virtual void decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
ModifiablePixelBuffer* pb);
};
}
#endif

115
common/rfb/RREEncoder.cxx Normal file
View File

@@ -0,0 +1,115 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. 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 <rdr/OutStream.h>
#include <rfb/encodings.h>
#include <rfb/SConnection.h>
#include <rfb/PixelFormat.h>
#include <rfb/PixelBuffer.h>
#include <rfb/Palette.h>
#include <rfb/RREEncoder.h>
using namespace rfb;
#define BPP 8
#include <rfb/rreEncode.h>
#undef BPP
#define BPP 16
#include <rfb/rreEncode.h>
#undef BPP
#define BPP 32
#include <rfb/rreEncode.h>
#undef BPP
RREEncoder::RREEncoder(SConnection* conn) :
Encoder(conn, encodingRRE, EncoderPlain, -1)
{
}
RREEncoder::~RREEncoder()
{
}
bool RREEncoder::isSupported()
{
return conn->cp.supportsEncoding(encodingRRE);
}
void RREEncoder::writeRect(const PixelBuffer* pb, const Palette& palette)
{
rdr::U8* imageBuf;
int stride;
rdr::U32 bg;
int w = pb->width();
int h = pb->height();
if (palette.size() == 1) {
Encoder::writeSolidRect(pb, palette);
return;
}
// We have to have our own copy of the data as we modify it as
// we find subrects.
bufferCopy.setPF(pb->getPF());
bufferCopy.setSize(w, h);
imageBuf = bufferCopy.getBufferRW(pb->getRect(), &stride);
pb->getImage(imageBuf, pb->getRect());
if (palette.size() > 0)
bg = palette.getColour(0);
else {
// Some crazy person is using this encoder for high colour
// data. Just pick the first pixel as the background colour.
bg = 0;
memcpy(&bg, imageBuf, pb->getPF().bpp/8);
}
int nSubrects = -1;
switch (pb->getPF().bpp) {
case 8:
nSubrects = rreEncode8((rdr::U8*)imageBuf, w, h, &mos, bg);
break;
case 16:
nSubrects = rreEncode16((rdr::U16*)imageBuf, w, h, &mos, bg);
break;
case 32:
nSubrects = rreEncode32((rdr::U32*)imageBuf, w, h, &mos, bg);
break;
}
bufferCopy.commitBufferRW(pb->getRect());
rdr::OutStream* os = conn->getOutStream();
os->writeU32(nSubrects);
os->writeBytes(mos.data(), mos.length());
mos.clear();
}
void RREEncoder::writeSolidRect(int width, int height,
const PixelFormat& pf,
const rdr::U8* colour)
{
rdr::OutStream* os;
os = conn->getOutStream();
os->writeU32(0);
os->writeBytes(colour, pf.bpp/8);
}

42
common/rfb/RREEncoder.h Normal file
View File

@@ -0,0 +1,42 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. 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.
*/
#ifndef __RFB_RREENCODER_H__
#define __RFB_RREENCODER_H__
#include <rdr/MemOutStream.h>
#include <rfb/Encoder.h>
#include <rfb/PixelBuffer.h>
namespace rfb {
class RREEncoder : public Encoder {
public:
RREEncoder(SConnection* conn);
virtual ~RREEncoder();
virtual bool isSupported();
virtual void writeRect(const PixelBuffer* pb, const Palette& palette);
virtual void writeSolidRect(int width, int height,
const PixelFormat& pf,
const rdr::U8* colour);
private:
rdr::MemOutStream mos;
ManagedPixelBuffer bufferCopy;
};
}
#endif

48
common/rfb/RawDecoder.cxx Normal file
View File

@@ -0,0 +1,48 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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 <assert.h>
#include <rdr/OutStream.h>
#include <rfb/ConnParams.h>
#include <rfb/PixelBuffer.h>
#include <rfb/RawDecoder.h>
using namespace rfb;
RawDecoder::RawDecoder() : Decoder(DecoderPlain)
{
}
RawDecoder::~RawDecoder()
{
}
void RawDecoder::readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os)
{
os->copyBytes(is, r.area() * (cp.pf().bpp/8));
}
void RawDecoder::decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
ModifiablePixelBuffer* pb)
{
assert(buflen >= (size_t)r.area() * (cp.pf().bpp/8));
pb->imageRect(cp.pf(), r, buffer);
}

35
common/rfb/RawDecoder.h Normal file
View File

@@ -0,0 +1,35 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
#ifndef __RFB_RAWDECODER_H__
#define __RFB_RAWDECODER_H__
#include <rfb/Decoder.h>
namespace rfb {
class RawDecoder : public Decoder {
public:
RawDecoder();
virtual ~RawDecoder();
virtual void readRect(const Rect& r, rdr::InStream* is,
const ConnParams& cp, rdr::OutStream* os);
virtual void decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ConnParams& cp,
ModifiablePixelBuffer* pb);
};
}
#endif

76
common/rfb/RawEncoder.cxx Normal file
View File

@@ -0,0 +1,76 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. 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 <rdr/OutStream.h>
#include <rfb/encodings.h>
#include <rfb/SConnection.h>
#include <rfb/PixelBuffer.h>
#include <rfb/RawEncoder.h>
using namespace rfb;
RawEncoder::RawEncoder(SConnection* conn) :
Encoder(conn, encodingRaw, EncoderPlain, -1)
{
}
RawEncoder::~RawEncoder()
{
}
bool RawEncoder::isSupported()
{
// Implicitly required;
return true;
}
void RawEncoder::writeRect(const PixelBuffer* pb, const Palette& palette)
{
const rdr::U8* buffer;
int stride;
rdr::OutStream* os;
int h, line_bytes, stride_bytes;
buffer = pb->getBuffer(pb->getRect(), &stride);
os = conn->getOutStream();
h = pb->height();
line_bytes = pb->width() * pb->getPF().bpp/8;
stride_bytes = stride * pb->getPF().bpp/8;
while (h--) {
os->writeBytes(buffer, line_bytes);
buffer += stride_bytes;
}
}
void RawEncoder::writeSolidRect(int width, int height,
const PixelFormat& pf,
const rdr::U8* colour)
{
rdr::OutStream* os;
int pixels, pixel_size;
os = conn->getOutStream();
pixels = width*height;
pixel_size = pf.bpp/8;
while (pixels--)
os->writeBytes(colour, pixel_size);
}

37
common/rfb/RawEncoder.h Normal file
View File

@@ -0,0 +1,37 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. 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.
*/
#ifndef __RFB_RAWENCODER_H__
#define __RFB_RAWENCODER_H__
#include <rfb/Encoder.h>
namespace rfb {
class RawEncoder : public Encoder {
public:
RawEncoder(SConnection* conn);
virtual ~RawEncoder();
virtual bool isSupported();
virtual void writeRect(const PixelBuffer* pb, const Palette& palette);
virtual void writeSolidRect(int width, int height,
const PixelFormat& pf,
const rdr::U8* colour);
};
}
#endif

116
common/rfb/Rect.h Normal file
View File

@@ -0,0 +1,116 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
// rfb::Rect and rfb::Point structures
#ifndef __RFB_RECT_INCLUDED__
#define __RFB_RECT_INCLUDED__
// Some platforms (e.g. Windows) include max() and min() macros in their
// standard headers, but they are also standard C++ template functions, so some
// C++ headers will undefine them. So we steer clear of the names min and max
// and define __rfbmin and __rfbmax instead.
#ifndef __rfbmax
#define __rfbmax(a,b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef __rfbmin
#define __rfbmin(a,b) (((a) < (b)) ? (a) : (b))
#endif
namespace rfb {
// rfb::Point
//
// Represents a point in 2D space, by X and Y coordinates.
// Can also be used to represent a delta, or offset, between
// two Points.
// Functions are provided to allow Points to be compared for
// equality and translated by a supplied offset.
// Functions are also provided to negate offset Points.
struct Point {
Point() : x(0), y(0) {}
Point(int x_, int y_) : x(x_), y(y_) {}
inline Point negate() const {return Point(-x, -y);}
inline bool equals(const Point &p) const {return x==p.x && y==p.y;}
inline Point translate(const Point &p) const {return Point(x+p.x, y+p.y);}
inline Point subtract(const Point &p) const {return Point(x-p.x, y-p.y);}
int x, y;
};
// rfb::Rect
//
// Represents a rectangular region defined by its top-left (tl)
// and bottom-right (br) Points.
// Rects may be compared for equality, checked to determine whether
// or not they are empty, cleared (made empty), or intersected with
// one another. The bounding rectangle of two existing Rects
// may be calculated, as may the area of a Rect.
// Rects may also be translated, in the same way as Points, by
// an offset specified in a Point structure.
struct Rect {
Rect() {}
Rect(Point tl_, Point br_) : tl(tl_), br(br_) {}
Rect(int x1, int y1, int x2, int y2) : tl(x1, y1), br(x2, y2) {}
inline void setXYWH(int x, int y, int w, int h) {
tl.x = x; tl.y = y; br.x = x+w; br.y = y+h;
}
inline Rect intersect(const Rect &r) const {
Rect result;
result.tl.x = __rfbmax(tl.x, r.tl.x);
result.tl.y = __rfbmax(tl.y, r.tl.y);
result.br.x = __rfbmax(__rfbmin(br.x, r.br.x), result.tl.x);
result.br.y = __rfbmax(__rfbmin(br.y, r.br.y), result.tl.y);
return result;
}
inline Rect union_boundary(const Rect &r) const {
if (r.is_empty()) return *this;
if (is_empty()) return r;
Rect result;
result.tl.x = __rfbmin(tl.x, r.tl.x);
result.tl.y = __rfbmin(tl.y, r.tl.y);
result.br.x = __rfbmax(br.x, r.br.x);
result.br.y = __rfbmax(br.y, r.br.y);
return result;
}
inline Rect translate(const Point &p) const {
return Rect(tl.translate(p), br.translate(p));
}
inline bool equals(const Rect &r) const {return r.tl.equals(tl) && r.br.equals(br);}
inline bool is_empty() const {return (tl.x >= br.x) || (tl.y >= br.y);}
inline void clear() {tl = Point(); br = Point();}
inline bool enclosed_by(const Rect &r) const {
return (tl.x>=r.tl.x) && (tl.y>=r.tl.y) && (br.x<=r.br.x) && (br.y<=r.br.y);
}
inline bool overlaps(const Rect &r) const {
return tl.x < r.br.x && tl.y < r.br.y && br.x > r.tl.x && br.y > r.tl.y;
}
inline int area() const {return is_empty() ? 0 : (br.x-tl.x)*(br.y-tl.y);}
inline Point dimensions() const {return Point(width(), height());}
inline int width() const {return br.x-tl.x;}
inline int height() const {return br.y-tl.y;}
inline bool contains(const Point &p) const {
return (tl.x<=p.x) && (tl.y<=p.y) && (br.x>p.x) && (br.y>p.y);
}
Point tl;
Point br;
};
}
#endif // __RFB_RECT_INCLUDED__

241
common/rfb/Region.cxx Normal file
View File

@@ -0,0 +1,241 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
// Cross-platform Region class based on the X11 region implementation. Note
// that for efficiency this code manipulates the Xlib region structure
// directly. Apart from the layout of the structure, there is one other key
// assumption made: a Region returned from XCreateRegion must always have its
// rects member allocated so that there is space for at least one rectangle.
//
#include <rfb/Region.h>
#include <assert.h>
#include <stdio.h>
extern "C" {
#include <Xregion/Xlibint.h>
#include <Xregion/Xutil.h>
#include <Xregion/Xregion.h>
}
// A _RectRegion must never be passed as a return parameter to the Xlib region
// operations. This is because for efficiency its "rects" member has not been
// allocated with Xmalloc. It is however safe to pass it as an input
// parameter.
class _RectRegion {
public:
_RectRegion(const rfb::Rect& r) {
region.rects = &region.extents;
region.numRects = 1;
region.extents.x1 = r.tl.x;
region.extents.y1 = r.tl.y;
region.extents.x2 = r.br.x;
region.extents.y2 = r.br.y;
region.size = 1;
if (r.is_empty())
region.numRects = 0;
}
REGION region;
};
rfb::Region::Region() {
xrgn = XCreateRegion();
assert(xrgn);
}
rfb::Region::Region(const Rect& r) {
xrgn = XCreateRegion();
assert(xrgn);
reset(r);
}
rfb::Region::Region(const rfb::Region& r) {
xrgn = XCreateRegion();
assert(xrgn);
XUnionRegion(xrgn, r.xrgn, xrgn);
}
rfb::Region::~Region() {
XDestroyRegion(xrgn);
}
rfb::Region& rfb::Region::operator=(const rfb::Region& r) {
clear();
XUnionRegion(xrgn, r.xrgn, xrgn);
return *this;
}
void rfb::Region::clear() {
xrgn->numRects = 0;
xrgn->extents.x1 = 0;
xrgn->extents.y1 = 0;
xrgn->extents.x2 = 0;
xrgn->extents.y2 = 0;
}
void rfb::Region::reset(const Rect& r) {
if (r.is_empty()) {
clear();
} else {
xrgn->numRects = 1;
xrgn->rects[0].x1 = xrgn->extents.x1 = r.tl.x;
xrgn->rects[0].y1 = xrgn->extents.y1 = r.tl.y;
xrgn->rects[0].x2 = xrgn->extents.x2 = r.br.x;
xrgn->rects[0].y2 = xrgn->extents.y2 = r.br.y;
}
}
void rfb::Region::translate(const Point& delta) {
XOffsetRegion(xrgn, delta.x, delta.y);
}
void rfb::Region::setOrderedRects(const std::vector<Rect>& rects) {
clear();
std::vector<Rect>::const_iterator i;
for (i=rects.begin(); i != rects.end(); i++) {
_RectRegion rr(*i);
XUnionRegion(xrgn, &rr.region, xrgn);
}
}
void rfb::Region::setExtentsAndOrderedRects(const ShortRect* extents,
int nRects, const ShortRect* rects)
{
if (xrgn->size < nRects)
{
BOX* prevRects = xrgn->rects;
xrgn->rects = (BOX*)Xrealloc((char*)xrgn->rects, nRects * sizeof(BOX));
if (!xrgn->rects) {
fprintf(stderr,"Xrealloc failed\n");
Xfree(prevRects);
return;
}
xrgn->size = nRects;
}
xrgn->numRects = nRects;
xrgn->extents.x1 = extents->x1;
xrgn->extents.y1 = extents->y1;
xrgn->extents.x2 = extents->x2;
xrgn->extents.y2 = extents->y2;
for (int i = 0; i < nRects; i++) {
xrgn->rects[i].x1 = rects[i].x1;
xrgn->rects[i].y1 = rects[i].y1;
xrgn->rects[i].x2 = rects[i].x2;
xrgn->rects[i].y2 = rects[i].y2;
}
}
void rfb::Region::assign_intersect(const rfb::Region& r) {
XIntersectRegion(xrgn, r.xrgn, xrgn);
}
void rfb::Region::assign_union(const rfb::Region& r) {
XUnionRegion(xrgn, r.xrgn, xrgn);
}
void rfb::Region::assign_subtract(const rfb::Region& r) {
XSubtractRegion(xrgn, r.xrgn, xrgn);
}
rfb::Region rfb::Region::intersect(const rfb::Region& r) const {
rfb::Region ret;
XIntersectRegion(xrgn, r.xrgn, ret.xrgn);
return ret;
}
rfb::Region rfb::Region::union_(const rfb::Region& r) const {
rfb::Region ret;
XUnionRegion(xrgn, r.xrgn, ret.xrgn);
return ret;
}
rfb::Region rfb::Region::subtract(const rfb::Region& r) const {
rfb::Region ret;
XSubtractRegion(xrgn, r.xrgn, ret.xrgn);
return ret;
}
bool rfb::Region::equals(const rfb::Region& r) const {
return XEqualRegion(xrgn, r.xrgn);
}
int rfb::Region::numRects() const {
return xrgn->numRects;
}
bool rfb::Region::get_rects(std::vector<Rect>* rects,
bool left2right, bool topdown) const
{
int nRects = xrgn->numRects;
int xInc = left2right ? 1 : -1;
int yInc = topdown ? 1 : -1;
int i = topdown ? 0 : nRects-1;
rects->clear();
rects->reserve(nRects);
while (nRects > 0) {
int firstInNextBand = i;
int nRectsInBand = 0;
while (nRects > 0 && xrgn->rects[firstInNextBand].y1 == xrgn->rects[i].y1)
{
firstInNextBand += yInc;
nRects--;
nRectsInBand++;
}
if (xInc != yInc)
i = firstInNextBand - yInc;
while (nRectsInBand > 0) {
Rect r(xrgn->rects[i].x1, xrgn->rects[i].y1,
xrgn->rects[i].x2, xrgn->rects[i].y2);
rects->push_back(r);
i += xInc;
nRectsInBand--;
}
i = firstInNextBand;
}
return !rects->empty();
}
rfb::Rect rfb::Region::get_bounding_rect() const {
return Rect(xrgn->extents.x1, xrgn->extents.y1,
xrgn->extents.x2, xrgn->extents.y2);
}
void rfb::Region::debug_print(const char* prefix) const
{
fprintf(stderr,"%s num rects %3ld extents %3d,%3d %3dx%3d\n",
prefix, xrgn->numRects, xrgn->extents.x1, xrgn->extents.y1,
xrgn->extents.x2-xrgn->extents.x1,
xrgn->extents.y2-xrgn->extents.y1);
for (int i = 0; i < xrgn->numRects; i++) {
fprintf(stderr," rect %3d,%3d %3dx%3d\n",
xrgn->rects[i].x1, xrgn->rects[i].y1,
xrgn->rects[i].x2-xrgn->rects[i].x1,
xrgn->rects[i].y2-xrgn->rects[i].y1);
}
}

83
common/rfb/Region.h Normal file
View File

@@ -0,0 +1,83 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
// Cross-platform Region class based on the X11 region implementation
#ifndef __RFB_REGION_INCLUDED__
#define __RFB_REGION_INCLUDED__
#include <rfb/Rect.h>
#include <vector>
struct _XRegion;
namespace rfb {
struct ShortRect {
short x1, y1, x2, y2;
};
class Region {
public:
// Create an empty region
Region();
// Create a rectangular region
Region(const Rect& r);
Region(const Region& r);
Region &operator=(const Region& src);
~Region();
// the following methods alter the region in place:
void clear();
void reset(const Rect& r);
void translate(const rfb::Point& delta);
void setOrderedRects(const std::vector<Rect>& rects);
void setExtentsAndOrderedRects(const ShortRect* extents, int nRects,
const ShortRect* rects);
void assign_intersect(const Region& r);
void assign_union(const Region& r);
void assign_subtract(const Region& r);
// the following three operations return a new region:
Region intersect(const Region& r) const;
Region union_(const Region& r) const;
Region subtract(const Region& r) const;
bool equals(const Region& b) const;
int numRects() const;
bool is_empty() const { return numRects() == 0; }
bool get_rects(std::vector<Rect>* rects, bool left2right=true,
bool topdown=true) const;
Rect get_bounding_rect() const;
void debug_print(const char *prefix) const;
protected:
struct _XRegion* xrgn;
};
};
#endif // __RFB_REGION_INCLUDED__

387
common/rfb/SConnection.cxx Normal file
View File

@@ -0,0 +1,387 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2011 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 <string.h>
#include <rfb/Exception.h>
#include <rfb/Security.h>
#include <rfb/msgTypes.h>
#include <rfb/fenceTypes.h>
#include <rfb/SMsgReader.h>
#include <rfb/SMsgWriter.h>
#include <rfb/SConnection.h>
#include <rfb/ServerCore.h>
#include <rfb/encodings.h>
#include <rfb/EncodeManager.h>
#include <rfb/SSecurity.h>
#include <rfb/LogWriter.h>
using namespace rfb;
static LogWriter vlog("SConnection");
// AccessRights values
const SConnection::AccessRights SConnection::AccessView = 0x0001;
const SConnection::AccessRights SConnection::AccessKeyEvents = 0x0002;
const SConnection::AccessRights SConnection::AccessPtrEvents = 0x0004;
const SConnection::AccessRights SConnection::AccessCutText = 0x0008;
const SConnection::AccessRights SConnection::AccessSetDesktopSize = 0x0010;
const SConnection::AccessRights SConnection::AccessNonShared = 0x0020;
const SConnection::AccessRights SConnection::AccessDefault = 0x03ff;
const SConnection::AccessRights SConnection::AccessNoQuery = 0x0400;
const SConnection::AccessRights SConnection::AccessFull = 0xffff;
SConnection::SConnection()
: readyForSetColourMapEntries(false),
is(0), os(0), reader_(0), writer_(0),
ssecurity(0), state_(RFBSTATE_UNINITIALISED),
preferredEncoding(encodingRaw)
{
defaultMajorVersion = 3;
defaultMinorVersion = 8;
if (rfb::Server::protocol3_3)
defaultMinorVersion = 3;
cp.setVersion(defaultMajorVersion, defaultMinorVersion);
}
SConnection::~SConnection()
{
if (ssecurity) ssecurity->destroy();
delete reader_;
reader_ = 0;
delete writer_;
writer_ = 0;
}
void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
{
is = is_;
os = os_;
}
void SConnection::initialiseProtocol()
{
cp.writeVersion(os);
state_ = RFBSTATE_PROTOCOL_VERSION;
}
void SConnection::processMsg()
{
switch (state_) {
case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
case RFBSTATE_SECURITY_TYPE: processSecurityTypeMsg(); break;
case RFBSTATE_SECURITY: processSecurityMsg(); break;
case RFBSTATE_INITIALISATION: processInitMsg(); break;
case RFBSTATE_NORMAL: reader_->readMsg(); break;
case RFBSTATE_QUERYING:
throw Exception("SConnection::processMsg: bogus data from client while "
"querying");
case RFBSTATE_UNINITIALISED:
throw Exception("SConnection::processMsg: not initialised yet?");
default:
throw Exception("SConnection::processMsg: invalid state");
}
}
void SConnection::processVersionMsg()
{
vlog.debug("reading protocol version");
bool done;
if (!cp.readVersion(is, &done)) {
state_ = RFBSTATE_INVALID;
throw Exception("reading version failed: not an RFB client?");
}
if (!done) return;
vlog.info("Client needs protocol version %d.%d",
cp.majorVersion, cp.minorVersion);
if (cp.majorVersion != 3) {
// unknown protocol version
throwConnFailedException("Client needs protocol version %d.%d, server has %d.%d",
cp.majorVersion, cp.minorVersion,
defaultMajorVersion, defaultMinorVersion);
}
if (cp.minorVersion != 3 && cp.minorVersion != 7 && cp.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;
else
cp.minorVersion = 3;
vlog.error("Assuming compatibility with version %d.%d",
cp.majorVersion,cp.minorVersion);
}
versionReceived();
std::list<rdr::U8> secTypes;
std::list<rdr::U8>::iterator i;
secTypes = security.GetEnabledSecTypes();
if (cp.isVersion(3,3)) {
// cope with legacy 3.3 client only if "no authentication" or "vnc
// authentication" is supported.
for (i=secTypes.begin(); i!=secTypes.end(); i++) {
if (*i == secTypeNone || *i == secTypeVncAuth) break;
}
if (i == secTypes.end()) {
throwConnFailedException("No supported security type for %d.%d client",
cp.majorVersion, cp.minorVersion);
}
os->writeU32(*i);
if (*i == secTypeNone) os->flush();
state_ = RFBSTATE_SECURITY;
ssecurity = security.GetSSecurity(*i);
processSecurityMsg();
return;
}
// list supported security types for >=3.7 clients
if (secTypes.empty())
throwConnFailedException("No supported security types");
os->writeU8(secTypes.size());
for (i=secTypes.begin(); i!=secTypes.end(); i++)
os->writeU8(*i);
os->flush();
state_ = RFBSTATE_SECURITY_TYPE;
}
void SConnection::processSecurityTypeMsg()
{
vlog.debug("processing security type message");
int secType = is->readU8();
processSecurityType(secType);
}
void SConnection::processSecurityType(int secType)
{
// Verify that the requested security type should be offered
std::list<rdr::U8> secTypes;
std::list<rdr::U8>::iterator i;
secTypes = security.GetEnabledSecTypes();
for (i=secTypes.begin(); i!=secTypes.end(); i++)
if (*i == secType) break;
if (i == secTypes.end())
throw Exception("Requested security type not available");
vlog.info("Client requests security type %s(%d)",
secTypeName(secType),secType);
try {
state_ = RFBSTATE_SECURITY;
ssecurity = security.GetSSecurity(secType);
} catch (rdr::Exception& e) {
throwConnFailedException("%s", e.str());
}
processSecurityMsg();
}
void SConnection::processSecurityMsg()
{
vlog.debug("processing security message");
try {
bool done = ssecurity->processMsg(this);
if (done) {
state_ = RFBSTATE_QUERYING;
setAccessRights(ssecurity->getAccessRights());
queryConnection(ssecurity->getUserName());
}
} catch (AuthFailureException& e) {
vlog.error("AuthFailureException: %s", e.str());
os->writeU32(secResultFailed);
if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
os->writeString(e.str());
os->flush();
throw;
}
}
void SConnection::processInitMsg()
{
vlog.debug("reading client initialisation");
reader_->readClientInit();
}
void SConnection::throwConnFailedException(const char* format, ...)
{
va_list ap;
char str[256];
va_start(ap, format);
(void) vsnprintf(str, sizeof(str), format, ap);
va_end(ap);
vlog.info("Connection failed: %s", str);
if (state_ == RFBSTATE_PROTOCOL_VERSION) {
if (cp.majorVersion == 3 && cp.minorVersion == 3) {
os->writeU32(0);
os->writeString(str);
os->flush();
} else {
os->writeU8(0);
os->writeString(str);
os->flush();
}
}
state_ = RFBSTATE_INVALID;
throw ConnFailedException(str);
}
void SConnection::writeConnFailedFromScratch(const char* msg,
rdr::OutStream* os)
{
os->writeBytes("RFB 003.003\n", 12);
os->writeU32(0);
os->writeString(msg);
os->flush();
}
void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings)
{
int i;
preferredEncoding = encodingRaw;
for (i = 0;i < nEncodings;i++) {
if (EncodeManager::supported(encodings[i])) {
preferredEncoding = encodings[i];
break;
}
}
SMsgHandler::setEncodings(nEncodings, encodings);
}
void SConnection::supportsQEMUKeyEvent()
{
writer()->writeQEMUKeyEvent();
}
void SConnection::versionReceived()
{
}
void SConnection::authSuccess()
{
}
void SConnection::queryConnection(const char* userName)
{
approveConnection(true);
}
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 (accept) {
os->writeU32(secResultOK);
} else {
os->writeU32(secResultFailed);
if (!cp.beforeVersion(3,8)) { // 3.8 onwards have failure message
if (reason)
os->writeString(reason);
else
os->writeString("Authentication failure");
}
}
os->flush();
}
if (accept) {
state_ = RFBSTATE_INITIALISATION;
reader_ = new SMsgReader(this, is);
writer_ = new SMsgWriter(&cp, os);
authSuccess();
} else {
state_ = RFBSTATE_INVALID;
if (reason)
throw AuthFailureException(reason);
else
throw AuthFailureException();
}
}
void SConnection::clientInit(bool shared)
{
writer_->writeServerInit();
state_ = RFBSTATE_NORMAL;
}
void SConnection::setPixelFormat(const PixelFormat& pf)
{
SMsgHandler::setPixelFormat(pf);
readyForSetColourMapEntries = true;
if (!pf.trueColour)
writeFakeColourMap();
}
void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
{
if (!readyForSetColourMapEntries) {
readyForSetColourMapEntries = true;
if (!cp.pf().trueColour) {
writeFakeColourMap();
}
}
}
void SConnection::fence(rdr::U32 flags, unsigned len, const char data[])
{
if (!(flags & fenceFlagRequest))
return;
// We cannot guarantee any synchronisation at this level
flags = 0;
writer()->writeFence(flags, len, data);
}
void SConnection::enableContinuousUpdates(bool enable,
int x, int y, int w, int h)
{
}
void SConnection::writeFakeColourMap(void)
{
int i;
rdr::U16 red[256], green[256], blue[256];
for (i = 0;i < 256;i++)
cp.pf().rgbFromPixel(i, &red[i], &green[i], &blue[i]);
writer()->writeSetColourMapEntries(0, 256, red, green, blue);
}

208
common/rfb/SConnection.h Normal file
View File

@@ -0,0 +1,208 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2011 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.
*/
//
// SConnection - class on the server side representing a connection to a
// client. A derived class should override methods appropriately.
//
#ifndef __RFB_SCONNECTION_H__
#define __RFB_SCONNECTION_H__
#include <rdr/InStream.h>
#include <rdr/OutStream.h>
#include <rfb/SMsgHandler.h>
#include <rfb/SecurityServer.h>
namespace rfb {
class SMsgReader;
class SMsgWriter;
class SSecurity;
class SConnection : public SMsgHandler {
public:
SConnection();
virtual ~SConnection();
// Methods to initialise the connection
// setStreams() sets the streams to be used for the connection. These must
// be set before initialiseProtocol() and processMsg() are called. The
// SSecurity object may call setStreams() again to provide alternative
// streams over which the RFB protocol is sent (i.e. encrypting/decrypting
// streams). Ownership of the streams remains with the caller
// (i.e. SConnection will not delete them).
void setStreams(rdr::InStream* is, rdr::OutStream* os);
// initialiseProtocol() should be called once the streams and security
// types are set. Subsequently, processMsg() should be called whenever
// there is data to read on the InStream.
void initialiseProtocol();
// processMsg() should be called whenever there is data to read on the
// InStream. You must have called initialiseProtocol() first.
void processMsg();
// approveConnection() is called to either accept or reject the connection.
// If accept is false, the reason string gives the reason for the
// rejection. It can either be called directly from queryConnection() or
// later, after queryConnection() has returned. It can only be called when
// in state RFBSTATE_QUERYING. On rejection, an AuthFailureException is
// thrown, so this must be handled appropriately by the caller.
void approveConnection(bool accept, const char* reason=0);
// Overridden from SMsgHandler
virtual void setEncodings(int nEncodings, const rdr::S32* encodings);
virtual void supportsQEMUKeyEvent();
// Methods to be overridden in a derived class
// versionReceived() indicates that the version number has just been read
// from the client. The version will already have been "cooked"
// to deal with unknown/bogus viewer protocol numbers.
virtual void versionReceived();
// authSuccess() is called when authentication has succeeded.
virtual void authSuccess();
// queryConnection() is called when authentication has succeeded, but
// before informing the client. It can be overridden to query a local user
// to accept the incoming connection, for example. The userName argument
// is the name of the user making the connection, or null (note that the
// storage for userName is owned by the caller). The connection must be
// accepted or rejected by calling approveConnection(), either directly
// from queryConnection() or some time later.
virtual void queryConnection(const char* userName);
// clientInit() is called when the ClientInit message is received. The
// derived class must call on to SConnection::clientInit().
virtual void clientInit(bool shared);
// setPixelFormat() is called when a SetPixelFormat message is received.
// The derived class must call on to SConnection::setPixelFormat().
virtual void setPixelFormat(const PixelFormat& pf);
// framebufferUpdateRequest() is called when a FramebufferUpdateRequest
// message is received. The derived class must call on to
// SConnection::framebufferUpdateRequest().
virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
// fence() is called when we get a fence request or response. By default
// it responds directly to requests (stating it doesn't support any
// synchronisation) and drops responses. Override to implement more proper
// support.
virtual void fence(rdr::U32 flags, unsigned len, const char data[]);
// enableContinuousUpdates() is called when the client wants to enable
// or disable continuous updates, or change the active area.
virtual void enableContinuousUpdates(bool enable,
int x, int y, int w, int h);
virtual void add_changed_all() {}
// setAccessRights() allows a security package to limit the access rights
// of a VNCSConnectionST to the server. How the access rights are treated
// is up to the derived class.
typedef rdr::U16 AccessRights;
static const AccessRights AccessView; // View display contents
static const AccessRights AccessKeyEvents; // Send key events
static const AccessRights AccessPtrEvents; // Send pointer events
static const AccessRights AccessCutText; // Send/receive clipboard events
static const AccessRights AccessSetDesktopSize; // Change desktop size
static const AccessRights AccessNonShared; // Exclusive access to the server
static const AccessRights AccessDefault; // The default rights, INCLUDING FUTURE ONES
static const AccessRights AccessNoQuery; // Connect without local user accepting
static const AccessRights AccessFull; // All of the available AND FUTURE rights
virtual void setAccessRights(AccessRights ar) = 0;
// Other methods
// authenticated() returns true if the client has authenticated
// successfully.
bool authenticated() { return (state_ == RFBSTATE_INITIALISATION ||
state_ == RFBSTATE_NORMAL); }
// throwConnFailedException() prints a message to the log, sends a conn
// failed message to the client (if possible) and throws a
// ConnFailedException.
void throwConnFailedException(const char* format, ...) __printf_attr(2, 3);
// writeConnFailedFromScratch() sends a conn failed message to an OutStream
// without the need to negotiate the protocol version first. It actually
// does this by assuming that the client will understand version 3.3 of the
// protocol.
static void writeConnFailedFromScratch(const char* msg,
rdr::OutStream* os);
SMsgReader* reader() { return reader_; }
SMsgWriter* writer() { return writer_; }
rdr::InStream* getInStream() { return is; }
rdr::OutStream* getOutStream() { return os; }
enum stateEnum {
RFBSTATE_UNINITIALISED,
RFBSTATE_PROTOCOL_VERSION,
RFBSTATE_SECURITY_TYPE,
RFBSTATE_SECURITY,
RFBSTATE_QUERYING,
RFBSTATE_INITIALISATION,
RFBSTATE_NORMAL,
RFBSTATE_CLOSING,
RFBSTATE_INVALID
};
stateEnum state() { return state_; }
rdr::S32 getPreferredEncoding() { return preferredEncoding; }
protected:
void setState(stateEnum s) { state_ = s; }
void setReader(SMsgReader *r) { reader_ = r; }
void setWriter(SMsgWriter *w) { writer_ = w; }
private:
void writeFakeColourMap(void);
bool readyForSetColourMapEntries;
void processVersionMsg();
void processSecurityTypeMsg();
void processSecurityType(int secType);
void processSecurityMsg();
void processInitMsg();
int defaultMajorVersion, defaultMinorVersion;
rdr::InStream* is;
rdr::OutStream* os;
SMsgReader* reader_;
SMsgWriter* writer_;
SecurityServer security;
SSecurity* ssecurity;
stateEnum state_;
rdr::S32 preferredEncoding;
};
}
#endif

123
common/rfb/SDesktop.h Normal file
View File

@@ -0,0 +1,123 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
*
* 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.
*/
/////////////////////////////////////////////////////////////////////////////
// SDesktop is an interface implemented by back-ends, on which callbacks are
// made by the VNCServer as appropriate for pointer and keyboard events, etc.
// SDesktop objects are always created before the VNCServer - the SDesktop
// will be passed a pointer to the VNCServer in the start() call. If a more
// implementation-specific pointer to the VNCServer is required then this
// can be provided to the SDesktop via an implementation-specific method.
//
// An SDesktop usually has an associated PixelBuffer which it tells the
// VNCServer via the VNCServer's setPixelBuffer() method. It can do this at
// any time, but the PixelBuffer MUST be valid by the time the call to start()
// returns. The PixelBuffer may be set to null again if desired when stop() is
// called. Note that start() and stop() are guaranteed to be called
// alternately; there should never be two calls to start() without an
// intervening stop() and vice-versa.
//
#ifndef __RFB_SDESKTOP_H__
#define __RFB_SDESKTOP_H__
#include <rfb/PixelBuffer.h>
#include <rfb/VNCServer.h>
#include <rfb/InputHandler.h>
#include <rfb/Exception.h>
#include <rfb/screenTypes.h>
#include <rfb/util.h>
namespace rfb {
class VNCServer;
class SDesktop : public InputHandler {
public:
// start() is called by the server when the first client authenticates
// successfully, and can be used to begin any expensive tasks which are not
// needed when there are no clients. A valid PixelBuffer must have been
// set via the VNCServer's setPixelBuffer() method by the time this call
// returns.
virtual void start(VNCServer* __unused_attr vs) {}
// stop() is called by the server when there are no longer any
// authenticated clients, and therefore the desktop can cease any
// expensive tasks. No further calls to the VNCServer passed to start()
// can be made once stop has returned.
virtual void stop() {}
// setScreenLayout() requests to reconfigure the framebuffer and/or
// the layout of screens.
virtual unsigned int setScreenLayout(int __unused_attr fb_width,
int __unused_attr fb_height,
const ScreenSet& __unused_attr layout) {
return resultProhibited;
}
// InputHandler interface
// pointerEvent(), keyEvent() and clientCutText() are called in response to
// the relevant RFB protocol messages from clients.
// See InputHandler for method signatures.
protected:
virtual ~SDesktop() {}
};
// -=- SStaticDesktop
// Trivial implementation of the SDesktop interface, which provides
// dummy input handlers and event processing routine, and exports
// a plain black desktop of the specified format.
class SStaticDesktop : public SDesktop {
public:
SStaticDesktop(const Point& size) : server(0), buffer(0) {
PixelFormat pf;
const rdr::U8 black[4] = { 0, 0, 0, 0 };
buffer = new ManagedPixelBuffer(pf, size.x, size.y);
if (buffer)
buffer->fillRect(buffer->getRect(), black);
}
SStaticDesktop(const Point& size, const PixelFormat& pf) : buffer(0) {
const rdr::U8 black[4] = { 0, 0, 0, 0 };
buffer = new ManagedPixelBuffer(pf, size.x, size.y);
if (buffer)
buffer->fillRect(buffer->getRect(), black);
}
virtual ~SStaticDesktop() {
if (buffer) delete buffer;
}
virtual void start(VNCServer* vs) {
server = vs;
server->setPixelBuffer(buffer);
}
virtual void stop() {
server->setPixelBuffer(0);
server = 0;
}
protected:
VNCServer* server;
ManagedPixelBuffer* buffer;
};
};
#endif // __RFB_SDESKTOP_H__

View File

@@ -0,0 +1,93 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-2011 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/SMsgHandler.h>
#include <rfb/ScreenSet.h>
using namespace rfb;
SMsgHandler::SMsgHandler()
{
}
SMsgHandler::~SMsgHandler()
{
}
void SMsgHandler::clientInit(bool shared)
{
}
void SMsgHandler::setPixelFormat(const PixelFormat& pf)
{
cp.setPF(pf);
}
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;
cp.setEncodings(nEncodings, encodings);
supportsLocalCursor();
if (cp.supportsFence && firstFence)
supportsFence();
if (cp.supportsContinuousUpdates && firstContinuousUpdates)
supportsContinuousUpdates();
if (cp.supportsLEDState && firstLEDState)
supportsLEDState();
if (cp.supportsQEMUKeyEvent && firstQEMUKeyEvent)
supportsQEMUKeyEvent();
}
void SMsgHandler::supportsLocalCursor()
{
}
void SMsgHandler::supportsFence()
{
}
void SMsgHandler::supportsContinuousUpdates()
{
}
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;
}

91
common/rfb/SMsgHandler.h Normal file
View File

@@ -0,0 +1,91 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-2011 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.
*/
//
// SMsgHandler - class to handle incoming messages on the server side.
//
#ifndef __RFB_SMSGHANDLER_H__
#define __RFB_SMSGHANDLER_H__
#include <rdr/types.h>
#include <rfb/PixelFormat.h>
#include <rfb/ConnParams.h>
#include <rfb/InputHandler.h>
#include <rfb/ScreenSet.h>
namespace rdr { class InStream; }
namespace rfb {
class SMsgHandler : public InputHandler {
public:
SMsgHandler();
virtual ~SMsgHandler();
// 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.
virtual void clientInit(bool shared);
virtual void setPixelFormat(const PixelFormat& pf);
virtual void setEncodings(int nEncodings, const rdr::S32* encodings);
virtual void framebufferUpdateRequest(const Rect& r, bool incremental) = 0;
virtual void setDesktopSize(int fb_width, int fb_height,
const ScreenSet& layout) = 0;
virtual void fence(rdr::U32 flags, unsigned len, const char data[]) = 0;
virtual void enableContinuousUpdates(bool enable,
int x, int y, int w, int h) = 0;
// InputHandler interface
// The InputHandler methods will be called for the corresponding messages.
// supportsLocalCursor() is called whenever the status of
// cp.supportsLocalCursor has changed. At the moment this happens on a
// setEncodings message, but in the future this may be due to a message
// specially for this purpose.
virtual void supportsLocalCursor();
// supportsFence() is called the first time we detect support for fences
// in the client. A fence message should be sent at this point to notify
// the client of server support.
virtual void supportsFence();
// supportsContinuousUpdates() is called the first time we detect that
// the client wants the continuous updates extension. A
// EndOfContinuousUpdates message should be sent back to the client at
// this point if it is supported.
virtual void supportsContinuousUpdates();
// supportsLEDState() is called the first time we detect that the
// client supports the LED state extension. A LEDState message
// should be sent back to the client to inform it of the current
// server state.
virtual void supportsLEDState();
// supportsQEMUKeyEvent() is called the first time we detect that the
// client wants the QEMU Extended Key Event extension. The default
// handler will send a pseudo-rect back, signalling server support.
virtual void supportsQEMUKeyEvent();
ConnParams cp;
};
}
#endif

261
common/rfb/SMsgReader.cxx Normal file
View File

@@ -0,0 +1,261 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-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 <rfb/msgTypes.h>
#include <rfb/qemuTypes.h>
#include <rfb/Exception.h>
#include <rfb/util.h>
#include <rfb/SMsgHandler.h>
#include <rfb/SMsgReader.h>
#include <rfb/Configuration.h>
#include <rfb/LogWriter.h>
#include <rfb/ServerCore.h>
using namespace rfb;
static LogWriter vlog("SMsgReader");
static IntParameter maxCutText("MaxCutText", "Maximum permitted length of an incoming clipboard update", 256*1024);
SMsgReader::SMsgReader(SMsgHandler* handler_, rdr::InStream* is_)
: handler(handler_), is(is_)
{
}
SMsgReader::~SMsgReader()
{
}
void SMsgReader::readClientInit()
{
bool shared = is->readU8();
handler->clientInit(shared);
}
void SMsgReader::readMsg()
{
int msgType = is->readU8();
switch (msgType) {
case msgTypeSetPixelFormat:
readSetPixelFormat();
break;
case msgTypeSetEncodings:
readSetEncodings();
break;
case msgTypeSetDesktopSize:
readSetDesktopSize();
break;
case msgTypeSetMaxVideoResolution:
readSetMaxVideoResolution();
break;
case msgTypeFramebufferUpdateRequest:
readFramebufferUpdateRequest();
break;
case msgTypeEnableContinuousUpdates:
readEnableContinuousUpdates();
break;
case msgTypeClientFence:
readFence();
break;
case msgTypeKeyEvent:
readKeyEvent();
break;
case msgTypePointerEvent:
readPointerEvent();
break;
case msgTypeClientCutText:
readClientCutText();
break;
case msgTypeQEMUClientMessage:
readQEMUMessage();
break;
default:
fprintf(stderr, "unknown message type %d\n", msgType);
throw Exception("unknown message type");
}
}
void SMsgReader::readSetPixelFormat()
{
is->skip(3);
PixelFormat pf;
pf.read(is);
handler->setPixelFormat(pf);
}
void SMsgReader::readSetEncodings()
{
is->skip(1);
int nEncodings = is->readU16();
rdr::S32Array encodings(nEncodings);
for (int i = 0; i < nEncodings; i++)
encodings.buf[i] = is->readU32();
handler->setEncodings(nEncodings, encodings.buf);
}
void SMsgReader::readSetDesktopSize()
{
int width, height;
int screens, i;
rdr::U32 id, flags;
int sx, sy, sw, sh;
ScreenSet layout;
is->skip(1);
width = is->readU16();
height = is->readU16();
screens = is->readU8();
is->skip(1);
for (i = 0;i < screens;i++) {
id = is->readU32();
sx = is->readU16();
sy = is->readU16();
sw = is->readU16();
sh = is->readU16();
flags = is->readU32();
layout.add_screen(Screen(id, sx, sy, sw, sh, flags));
}
handler->setDesktopSize(width, height, layout);
}
void SMsgReader::readSetMaxVideoResolution()
{
unsigned int width, height;
char tmp[16];
width = is->readU16();
height = is->readU16();
if (!rfb::Server::ignoreClientSettingsKasm) {
sprintf(tmp, "%ux%u", width, height);
rfb::Server::maxVideoResolution.setParam(tmp);
}
}
void SMsgReader::readFramebufferUpdateRequest()
{
bool inc = is->readU8();
int x = is->readU16();
int y = is->readU16();
int w = is->readU16();
int h = is->readU16();
handler->framebufferUpdateRequest(Rect(x, y, x+w, y+h), inc);
}
void SMsgReader::readEnableContinuousUpdates()
{
bool enable;
int x, y, w, h;
enable = is->readU8();
x = is->readU16();
y = is->readU16();
w = is->readU16();
h = is->readU16();
handler->enableContinuousUpdates(enable, x, y, w, h);
}
void SMsgReader::readFence()
{
rdr::U32 flags;
rdr::U8 len;
char data[64];
is->skip(3);
flags = is->readU32();
len = is->readU8();
if (len > sizeof(data)) {
fprintf(stderr, "Ignoring fence with too large payload\n");
is->skip(len);
return;
}
is->readBytes(data, len);
handler->fence(flags, len, data);
}
void SMsgReader::readKeyEvent()
{
bool down = is->readU8();
is->skip(2);
rdr::U32 key = is->readU32();
handler->keyEvent(key, 0, down);
}
void SMsgReader::readPointerEvent()
{
int mask = is->readU8();
int x = is->readU16();
int y = is->readU16();
handler->pointerEvent(Point(x, y), mask);
}
void SMsgReader::readClientCutText()
{
is->skip(3);
int len = is->readU32();
if (len < 0) {
throw Exception("Cut text too long.");
}
if (len > maxCutText) {
is->skip(len);
vlog.error("Cut text too long (%d bytes) - ignoring", len);
return;
}
CharArray ca(len+1);
ca.buf[len] = 0;
is->readBytes(ca.buf, len);
handler->clientCutText(ca.buf, len);
}
void SMsgReader::readQEMUMessage()
{
int subType = is->readU8();
switch (subType) {
case qemuExtendedKeyEvent:
readQEMUKeyEvent();
break;
default:
throw Exception("unknown QEMU submessage type %d", subType);
}
}
void SMsgReader::readQEMUKeyEvent()
{
bool down = is->readU16();
rdr::U32 keysym = is->readU32();
rdr::U32 keycode = is->readU32();
if (!keycode) {
vlog.error("Key event without keycode - ignoring");
return;
}
handler->keyEvent(keysym, keycode, down);
}

66
common/rfb/SMsgReader.h Normal file
View File

@@ -0,0 +1,66 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-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.
*/
//
// SMsgReader - class for reading RFB messages on the server side
// (i.e. messages from client to server).
//
#ifndef __RFB_SMSGREADER_H__
#define __RFB_SMSGREADER_H__
namespace rdr { class InStream; }
namespace rfb {
class SMsgHandler;
class SMsgReader {
public:
SMsgReader(SMsgHandler* handler, rdr::InStream* is);
virtual ~SMsgReader();
void readClientInit();
// readMsg() reads a message, calling the handler as appropriate.
void readMsg();
rdr::InStream* getInStream() { return is; }
protected:
void readSetPixelFormat();
void readSetEncodings();
void readSetDesktopSize();
void readSetMaxVideoResolution();
void readFramebufferUpdateRequest();
void readEnableContinuousUpdates();
void readFence();
void readKeyEvent();
void readPointerEvent();
void readClientCutText();
void readQEMUMessage();
void readQEMUKeyEvent();
SMsgHandler* handler;
rdr::InStream* is;
};
}
#endif

Some files were not shown because too many files have changed in this diff Show More