Initial commit
This commit is contained in:
86
common/rfb/Blacklist.cxx
Normal file
86
common/rfb/Blacklist.cxx
Normal 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
91
common/rfb/Blacklist.h
Normal 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
366
common/rfb/CConnection.cxx
Normal 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
195
common/rfb/CConnection.h
Normal 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
105
common/rfb/CMakeLists.txt
Normal 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()
|
||||
94
common/rfb/CMsgHandler.cxx
Normal file
94
common/rfb/CMsgHandler.cxx
Normal 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
78
common/rfb/CMsgHandler.h
Normal 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
398
common/rfb/CMsgReader.cxx
Normal 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
77
common/rfb/CMsgReader.h
Normal 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
276
common/rfb/CMsgWriter.cxx
Normal 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
67
common/rfb/CMsgWriter.h
Normal 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
61
common/rfb/CSecurity.h
Normal 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
|
||||
37
common/rfb/CSecurityNone.h
Normal file
37
common/rfb/CSecurityNone.h
Normal 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
|
||||
45
common/rfb/CSecurityPlain.cxx
Normal file
45
common/rfb/CSecurityPlain.cxx
Normal 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;
|
||||
}
|
||||
35
common/rfb/CSecurityPlain.h
Normal file
35
common/rfb/CSecurityPlain.h
Normal 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
|
||||
74
common/rfb/CSecurityStack.cxx
Normal file
74
common/rfb/CSecurityStack.cxx
Normal 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;
|
||||
}
|
||||
44
common/rfb/CSecurityStack.h
Normal file
44
common/rfb/CSecurityStack.h
Normal 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
445
common/rfb/CSecurityTLS.cxx
Normal 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
77
common/rfb/CSecurityTLS.h
Normal 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
|
||||
206
common/rfb/CSecurityVeNCrypt.cxx
Normal file
206
common/rfb/CSecurityVeNCrypt.cxx
Normal 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;
|
||||
}
|
||||
61
common/rfb/CSecurityVeNCrypt.h
Normal file
61
common/rfb/CSecurityVeNCrypt.h
Normal 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
|
||||
67
common/rfb/CSecurityVncAuth.cxx
Normal file
67
common/rfb/CSecurityVncAuth.cxx
Normal 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;
|
||||
}
|
||||
35
common/rfb/CSecurityVncAuth.h
Normal file
35
common/rfb/CSecurityVncAuth.h
Normal 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
|
||||
986
common/rfb/ComparingUpdateTracker.cxx
Normal file
986
common/rfb/ComparingUpdateTracker.cxx
Normal 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> ©PassRects,
|
||||
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();
|
||||
}
|
||||
65
common/rfb/ComparingUpdateTracker.h
Normal file
65
common/rfb/ComparingUpdateTracker.h
Normal 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
|
||||
505
common/rfb/Configuration.cxx
Normal file
505
common/rfb/Configuration.cxx
Normal 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 = ¤t->_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
312
common/rfb/Configuration.h
Normal 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
484
common/rfb/Congestion.cxx
Normal 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
95
common/rfb/Congestion.h
Normal 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
258
common/rfb/ConnParams.cxx
Normal 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
141
common/rfb/ConnParams.h
Normal 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
|
||||
65
common/rfb/CopyRectDecoder.cxx
Normal file
65
common/rfb/CopyRectDecoder.cxx
Normal 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));
|
||||
}
|
||||
39
common/rfb/CopyRectDecoder.h
Normal file
39
common/rfb/CopyRectDecoder.h
Normal 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
322
common/rfb/Cursor.cxx
Normal 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
73
common/rfb/Cursor.h
Normal 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
|
||||
365
common/rfb/DecodeManager.cxx
Normal file
365
common/rfb/DecodeManager.cxx
Normal 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
104
common/rfb/DecodeManager.h
Normal 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
88
common/rfb/Decoder.cxx
Normal 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
97
common/rfb/Decoder.h
Normal 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
72
common/rfb/EncCache.cxx
Normal 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
65
common/rfb/EncCache.h
Normal 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
1629
common/rfb/EncodeManager.cxx
Normal file
File diff suppressed because it is too large
Load Diff
194
common/rfb/EncodeManager.h
Normal file
194
common/rfb/EncodeManager.h
Normal 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> ©passed,
|
||||
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
|
||||
98
common/rfb/EncodeManagerBPP.cxx
Normal file
98
common/rfb/EncodeManagerBPP.cxx
Normal 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
81
common/rfb/Encoder.cxx
Normal 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
102
common/rfb/Encoder.h
Normal 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
42
common/rfb/Exception.h
Normal 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
424
common/rfb/HTTPServer.cxx
Normal 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
111
common/rfb/HTTPServer.h
Normal 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
|
||||
|
||||
104
common/rfb/HextileDecoder.cxx
Normal file
104
common/rfb/HextileDecoder.cxx
Normal 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;
|
||||
}
|
||||
}
|
||||
36
common/rfb/HextileDecoder.h
Normal file
36
common/rfb/HextileDecoder.h
Normal 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
|
||||
106
common/rfb/HextileEncoder.cxx
Normal file
106
common/rfb/HextileEncoder.cxx
Normal 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);
|
||||
}
|
||||
37
common/rfb/HextileEncoder.h
Normal file
37
common/rfb/HextileEncoder.h
Normal 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
100
common/rfb/Hostname.h
Normal 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
45
common/rfb/InputHandler.h
Normal 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
|
||||
254
common/rfb/JpegCompressor.cxx
Normal file
254
common/rfb/JpegCompressor.cxx
Normal 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.");
|
||||
}
|
||||
67
common/rfb/JpegCompressor.h
Normal file
67
common/rfb/JpegCompressor.h
Normal 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
|
||||
229
common/rfb/JpegDecompressor.cxx
Normal file
229
common/rfb/JpegDecompressor.cxx
Normal 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;
|
||||
}
|
||||
60
common/rfb/JpegDecompressor.h
Normal file
60
common/rfb/JpegDecompressor.h
Normal 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
|
||||
93
common/rfb/KeyRemapper.cxx
Normal file
93
common/rfb/KeyRemapper.cxx
Normal 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
43
common/rfb/KeyRemapper.h
Normal 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
126
common/rfb/ListConnInfo.h
Normal 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
134
common/rfb/LogWriter.cxx
Normal 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, ¶ms.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
122
common/rfb/LogWriter.h
Normal 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
81
common/rfb/Logger.cxx
Normal 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
76
common/rfb/Logger.h
Normal 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
120
common/rfb/Logger_file.cxx
Normal 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
54
common/rfb/Logger_file.h
Normal 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
|
||||
32
common/rfb/Logger_stdio.cxx
Normal file
32
common/rfb/Logger_stdio.cxx
Normal 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
39
common/rfb/Logger_stdio.h
Normal 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
|
||||
64
common/rfb/Logger_syslog.cxx
Normal file
64
common/rfb/Logger_syslog.cxx
Normal 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();
|
||||
}
|
||||
40
common/rfb/Logger_syslog.h
Normal file
40
common/rfb/Logger_syslog.h
Normal 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
190
common/rfb/Palette.h
Normal 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
80
common/rfb/Password.cxx
Normal 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
47
common/rfb/Password.h
Normal 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
26
common/rfb/Pixel.h
Normal 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
373
common/rfb/PixelBuffer.cxx
Normal 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
185
common/rfb/PixelBuffer.h
Normal 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
731
common/rfb/PixelFormat.cxx
Normal 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
156
common/rfb/PixelFormat.h
Normal 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
135
common/rfb/PixelFormat.inl
Normal 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];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
155
common/rfb/PixelFormatBPP.cxx
Normal file
155
common/rfb/PixelFormatBPP.cxx
Normal 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
69
common/rfb/RREDecoder.cxx
Normal 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
36
common/rfb/RREDecoder.h
Normal 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
115
common/rfb/RREEncoder.cxx
Normal 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
42
common/rfb/RREEncoder.h
Normal 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
48
common/rfb/RawDecoder.cxx
Normal 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
35
common/rfb/RawDecoder.h
Normal 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
76
common/rfb/RawEncoder.cxx
Normal 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
37
common/rfb/RawEncoder.h
Normal 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
116
common/rfb/Rect.h
Normal 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
241
common/rfb/Region.cxx
Normal 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 = ®ion.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
83
common/rfb/Region.h
Normal 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
387
common/rfb/SConnection.cxx
Normal 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
208
common/rfb/SConnection.h
Normal 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
123
common/rfb/SDesktop.h
Normal 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__
|
||||
93
common/rfb/SMsgHandler.cxx
Normal file
93
common/rfb/SMsgHandler.cxx
Normal 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
91
common/rfb/SMsgHandler.h
Normal 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
261
common/rfb/SMsgReader.cxx
Normal 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
66
common/rfb/SMsgReader.h
Normal 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
Reference in New Issue
Block a user