diff options
author | Constantin Kaplinsky <const@tightvnc.com> | 2006-05-25 05:01:55 +0000 |
---|---|---|
committer | Constantin Kaplinsky <const@tightvnc.com> | 2006-05-25 05:01:55 +0000 |
commit | a2adc8d4cfdf7336ce9192414c5e775224742a97 (patch) | |
tree | 0fc9f229bd40a2de342d91338798033da8ebd7bc /common/rfb | |
parent | 4fc2026b9595e9425f50616d18781995aebe495b (diff) | |
download | tigervnc-a2adc8d4cfdf7336ce9192414c5e775224742a97.tar.gz tigervnc-a2adc8d4cfdf7336ce9192414c5e775224742a97.zip |
Migrating to new directory structure adopted from the RealVNC's source tree. More changes will follow.
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@589 3789f03b-4d11-0410-bbf8-ca57d06f2519
Diffstat (limited to 'common/rfb')
166 files changed, 22473 insertions, 0 deletions
diff --git a/common/rfb/Blacklist.cxx b/common/rfb/Blacklist.cxx new file mode 100644 index 00000000..4590befe --- /dev/null +++ b/common/rfb/Blacklist.cxx @@ -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); + } +} diff --git a/common/rfb/Blacklist.h b/common/rfb/Blacklist.h new file mode 100644 index 00000000..0eb38460 --- /dev/null +++ b/common/rfb/Blacklist.h @@ -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 + diff --git a/common/rfb/CConnection.cxx b/common/rfb/CConnection.cxx new file mode 100644 index 00000000..36778f02 --- /dev/null +++ b/common/rfb/CConnection.cxx @@ -0,0 +1,276 @@ +/* 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 <string.h> +#include <rfb/Exception.h> +#include <rfb/CMsgReaderV3.h> +#include <rfb/CMsgWriterV3.h> +#include <rfb/CSecurity.h> +#include <rfb/secTypes.h> +#include <rfb/CConnection.h> +#include <rfb/util.h> + +#include <rfb/LogWriter.h> + +using namespace rfb; + +static LogWriter vlog("CConnection"); + +CConnection::CConnection() + : is(0), os(0), reader_(0), writer_(0), + shared(false), security(0), nSecTypes(0), clientSecTypeOrder(false), + state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false) +{ +} + +CConnection::~CConnection() +{ + if (security) security->destroy(); + deleteReaderAndWriter(); +} + +void CConnection::deleteReaderAndWriter() +{ + delete reader_; + reader_ = 0; + delete writer_; + writer_ = 0; +} + +void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_) +{ + is = is_; + os = os_; +} + +void CConnection::addSecType(rdr::U8 secType) +{ + if (nSecTypes == maxSecTypes) + throw Exception("too many security types"); + secTypes[nSecTypes++] = secType; +} + +void CConnection::setClientSecTypeOrder(bool clientOrder) { + clientSecTypeOrder = clientOrder; +} + +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)) { + char msg[256]; + sprintf(msg,"Server gave unsupported RFB protocol version %d.%d", + cp.majorVersion, cp.minorVersion); + vlog.error(msg); + state_ = RFBSTATE_INVALID; + throw Exception(msg); + } 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; + + 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) { + int j; + for (j = 0; j < nSecTypes; j++) + if (secTypes[j] == secType) break; + if (j == nSecTypes) + 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(); + + int secTypePos = nSecTypes; + for (int i = 0; i < nServerSecTypes; i++) { + rdr::U8 serverSecType = is->readU8(); + vlog.debug("Server offers security type %s(%d)", + secTypeName(serverSecType),serverSecType); + + // If we haven't already chosen a secType, try this one + // If we are using the client's preference for types, + // we keep trying types, to find the one that matches and + // which appears first in the client's list of supported types. + if (secType == secTypeInvalid || clientSecTypeOrder) { + for (int j = 0; j < nSecTypes; j++) { + if (secTypes[j] == serverSecType && j < secTypePos) { + secType = secTypes[j]; + secTypePos = j; + break; + } + } + // NB: Continue reading the remaining server secTypes, but ignore them + } + } + + // Inform the server of our decision + if (secType != secTypeInvalid) { + os->writeU8(secType); + os->flush(); + vlog.debug("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; + security = getCSecurity(secType); + processSecurityMsg(); +} + +void CConnection::processSecurityMsg() +{ + vlog.debug("processing security message"); + if (security->processMsg(this)) { + state_ = RFBSTATE_SECURITY_RESULT; + processSecurityResultMsg(); + } +} + +void CConnection::processSecurityResultMsg() +{ + vlog.debug("processing security result message"); + int result; + if (cp.beforeVersion(3,8) && security->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"); + } + CharArray reason; + if (cp.beforeVersion(3,8)) + reason.buf = strDup("Authentication failure"); + else + reason.buf = is->readString(); + state_ = RFBSTATE_INVALID; + 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 CMsgReaderV3(this, is); + writer_ = new CMsgWriterV3(&cp, os); + vlog.debug("Authentication success!"); + authSuccess(); + writer_->writeClientInit(shared); +} + +void CConnection::authSuccess() +{ +} + +void CConnection::serverInit() +{ + state_ = RFBSTATE_NORMAL; + vlog.debug("initialisation done"); +} diff --git a/common/rfb/CConnection.h b/common/rfb/CConnection.h new file mode 100644 index 00000000..79110eb9 --- /dev/null +++ b/common/rfb/CConnection.h @@ -0,0 +1,183 @@ +/* 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. + */ +// +// 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 <rdr/InStream.h> +#include <rdr/OutStream.h> +#include <rfb/CMsgHandler.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); + + // addSecType() should be called once for each security type which the + // client supports. The order in which they're added is such that the + // first one is most preferred. + void addSecType(rdr::U8 secType); + + // setClientSecTypeOrder() determines whether the client should obey + // the server's security type preference, by picking the first server security + // type that the client supports, or whether it should pick the first type + // that the server supports, from the client-supported list of types. + void setClientSecTypeOrder(bool clientOrder); + + // 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;} + + // 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 to be overridden in a derived class + + // getCSecurity() gets the CSecurity object for the given type. The type + // is guaranteed to be one of the secTypes passed in to addSecType(). The + // CSecurity object's destroy() method will be called by the CConnection + // from its destructor. + virtual CSecurity* getCSecurity(int secType)=0; + + // getCurrentCSecurity() gets the CSecurity instance used for this connection. + const CSecurity* getCurrentCSecurity() const {return security;} + + // 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 + + // deleteReaderAndWriter() deletes the reader and writer associated with + // this connection. This may be useful if you want to delete the streams + // before deleting the SConnection to make sure that no attempt by the + // SConnection is made to read or write. + // XXX Do we really need this at all??? + void deleteReaderAndWriter(); + + 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; } + + 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_; } + + protected: + void setState(stateEnum s) { state_ = s; } + + 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; + CSecurity* security; + enum { maxSecTypes = 8 }; + int nSecTypes; + rdr::U8 secTypes[maxSecTypes]; + bool clientSecTypeOrder; + stateEnum state_; + + CharArray serverName; + + bool useProtocol3_3; + }; +} +#endif diff --git a/common/rfb/CFTMsgReader.cxx b/common/rfb/CFTMsgReader.cxx new file mode 100644 index 00000000..5a17fc25 --- /dev/null +++ b/common/rfb/CFTMsgReader.cxx @@ -0,0 +1,162 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- CFTMsgReader.cxx + +#include <rfb/CFTMsgReader.h> + +using namespace rfb; + +CFTMsgReader::CFTMsgReader(rdr::InStream *pIS) +{ + m_pInStream = pIS; +} + +CFTMsgReader::~CFTMsgReader() +{ + +} + +int +CFTMsgReader::readFileListData(FileInfo *pFileInfo) +{ + unsigned char flags = m_pInStream->readU8(); + int numFiles = m_pInStream->readU16(); + int dataSize = m_pInStream->readU16(); + int compressedSize = m_pInStream->readU16(); + + if (flags & 0x80) { + return -1; + } else { + if (numFiles > 0) { + char *pFilenames = new char[compressedSize]; + SIZEDATAINFO *pSDI = new SIZEDATAINFO[numFiles]; + for (int i = 0; i < numFiles; i++) { + pSDI[i].size = m_pInStream->readU32(); + pSDI[i].data = m_pInStream->readU32(); + } + m_pInStream->readBytes((void *)pFilenames, compressedSize); + createFileInfo(numFiles, pFileInfo, pSDI, pFilenames); + delete [] pSDI; + delete [] pFilenames; + } + } + return numFiles; +} + +void * +CFTMsgReader::readFileDownloadData(unsigned int *pSize, unsigned int *pModTime) +{ + unsigned char compressLevel = m_pInStream->readU8(); + int realSize = m_pInStream->readU16(); + int compressedSize = m_pInStream->readU16(); + + if ((realSize == 0) && (compressedSize == 0)) { + *pSize = 0; + *pModTime = m_pInStream->readU32(); + return NULL; + } else { + char *pFile = new char [compressedSize]; + if (pFile == NULL) { + m_pInStream->skip(compressedSize); + *pModTime = 0; + return NULL; + } else { + m_pInStream->readBytes(pFile, compressedSize); + *pSize = compressedSize; + return pFile; + } + } +} + +char * +CFTMsgReader::readFileUploadCancel(unsigned int *pReasonSize) +{ + m_pInStream->skip(1); + return readReasonMsg(pReasonSize); +} + +char * +CFTMsgReader::readFileDownloadFailed(unsigned int *pReasonSize) +{ + m_pInStream->skip(1); + return readReasonMsg(pReasonSize); +} + +int +CFTMsgReader::readFileDirSizeData(unsigned short *pDirSizeLow16, + unsigned int *pDirSizeHigh32) +{ + m_pInStream->skip(1); + *pDirSizeLow16 = m_pInStream->readU16(); + *pDirSizeHigh32 = m_pInStream->readU32(); + return 1; +} + +char * +CFTMsgReader::readFileLastRqstFailed(int *pTypeOfRequest, unsigned int *pReasonSize) +{ + *pTypeOfRequest = m_pInStream->readU8(); + return readReasonMsg(pReasonSize); +} + +bool +CFTMsgReader::createFileInfo(unsigned int numFiles, FileInfo *fi, + SIZEDATAINFO *pSDInfo, char *pFilenames) +{ + int pos = 0; + int size = 0; + for (unsigned int i = 0; i < numFiles; i++) { + size = pSDInfo[i].size; + if (size == FT_NET_ATTR_DIR) { + fi->add((pFilenames + pos), size, pSDInfo[i].data, FT_ATTR_DIR); + } else { + fi->add((pFilenames + pos), size, pSDInfo[i].data, FT_ATTR_FILE); + } + pos += strlen(pFilenames + pos) + 1; + } + return true; +} + +char * +CFTMsgReader::readReasonMsg(unsigned int *pReasonSize) +{ + int reasonLen = m_pInStream->readU16(); + int _reasonLen = reasonLen + 1; + char *pReason; + if (reasonLen == 0) { + *pReasonSize = 0; + return NULL; + } else { + pReason = new char [_reasonLen]; + if (pReason == NULL) { + m_pInStream->skip(reasonLen); + *pReasonSize = 0; + return NULL; + } + m_pInStream->readBytes(pReason, reasonLen); + memset(((char *)pReason+reasonLen), '\0', 1); + return pReason; + } +} + diff --git a/common/rfb/CFTMsgReader.h b/common/rfb/CFTMsgReader.h new file mode 100644 index 00000000..aece3e78 --- /dev/null +++ b/common/rfb/CFTMsgReader.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- CFTMsgReader.h + +#ifndef __RFB_CFTMSGREADER_H__ +#define __RFB_CFTMSGREADER_H__ + +#include <rdr/InStream.h> +#include <rfb/FileInfo.h> + +namespace rfb { + class CFTMsgReader + { + public: + CFTMsgReader(rdr::InStream *pIS); + ~CFTMsgReader(); + + int readFileListData(FileInfo *pFileInfo); + int readFileDirSizeData(unsigned short *pDirSizeLow16, unsigned int *pDirSizeHigh32); + + char *readFileUploadCancel(unsigned int *pReasonSize); + char *readFileDownloadFailed(unsigned int *pReasonSize); + char *readFileLastRqstFailed(int *pTypeOfRequest, unsigned int *pReasonSize); + void *readFileDownloadData(unsigned int *pSize, unsigned int *pModTime); + + private: + rdr::InStream *m_pInStream; + + bool createFileInfo(unsigned int numFiles, FileInfo *fi, + SIZEDATAINFO *pSDInfo, char *pFilenames); + char *readReasonMsg(unsigned int *pReasonSize); + }; +} + +#endif // __RFB_CFTMSGREADER_H__ diff --git a/common/rfb/CFTMsgWriter.cxx b/common/rfb/CFTMsgWriter.cxx new file mode 100644 index 00000000..5e6854ed --- /dev/null +++ b/common/rfb/CFTMsgWriter.cxx @@ -0,0 +1,182 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- CFTMsgWriter.cxx + +#include <rfb/CFTMsgWriter.h> + +using namespace rfb; + +CFTMsgWriter::CFTMsgWriter(rdr::OutStream *pOS) +{ + m_pOutStream = pOS; +} + +CFTMsgWriter::~CFTMsgWriter() +{ +} + +bool +CFTMsgWriter::writeFileListRqst(unsigned short dirnameLen, char *pDirName, + bool bDirOnly) +{ + if (dirnameLen >= FT_FILENAME_SIZE) return false; + + unsigned char flags = 0; + if (bDirOnly) flags = 0x10; + + m_pOutStream->writeU8(msgTypeFileListRequest); + m_pOutStream->writeU8(flags); + m_pOutStream->writeU16(dirnameLen); + m_pOutStream->writeBytes((void *)pDirName, dirnameLen); + m_pOutStream->flush(); + + return true; +} + + +bool +CFTMsgWriter::writeFileDownloadCancel(unsigned short reasonLen, char *pReason) +{ + m_pOutStream->writeU8(msgTypeFileDownloadCancel); + return writeU8U16StringMsg(reasonLen, pReason); +} + +bool +CFTMsgWriter::writeFileDownloadRqst(unsigned short filenameLen, char *pFilename, + unsigned int position) +{ + if (filenameLen >= FT_FILENAME_SIZE) return false; + + m_pOutStream->writeU8(msgTypeFileDownloadRequest); + m_pOutStream->writeU8(0); + m_pOutStream->writeU16(filenameLen); + m_pOutStream->writeU32(position); + m_pOutStream->writeBytes(pFilename, filenameLen); + m_pOutStream->flush(); + + return true; +} + +bool +CFTMsgWriter::writeFileUploadData(unsigned short dataSize, char *pData) +{ + m_pOutStream->writeU8(msgTypeFileUploadData); + m_pOutStream->writeU8(0); + m_pOutStream->writeU16(dataSize); + m_pOutStream->writeU16(dataSize); + m_pOutStream->writeBytes(pData, dataSize); + m_pOutStream->flush(); + + return true; +} + +bool +CFTMsgWriter::writeFileUploadData(unsigned int modTime) +{ + m_pOutStream->writeU8(msgTypeFileUploadData); + m_pOutStream->writeU8(0); + m_pOutStream->writeU16(0); + m_pOutStream->writeU16(0); + m_pOutStream->writeU32(modTime); + m_pOutStream->flush(); + + return true; +} + +bool +CFTMsgWriter::writeFileUploadFailed(unsigned short reasonLen, char *pReason) +{ + m_pOutStream->writeU8(msgTypeFileUploadFailed); + return writeU8U16StringMsg(reasonLen, pReason); +} + +bool +CFTMsgWriter::writeFileUploadRqst(unsigned short filenameLen, char *pFilename, + unsigned int position) +{ + if (filenameLen >= FT_FILENAME_SIZE) return false; + + m_pOutStream->writeU8(msgTypeFileUploadRequest); + m_pOutStream->writeU8(0); + m_pOutStream->writeU16(filenameLen); + m_pOutStream->writeU32(position); + m_pOutStream->writeBytes((void *)pFilename, filenameLen); + m_pOutStream->flush(); + + return true; +} + +bool +CFTMsgWriter::writeFileCreateDirRqst(unsigned short dirNameLen, char *pDirName) +{ + if (dirNameLen >= FT_FILENAME_SIZE) return false; + + m_pOutStream->writeU8(msgTypeFileCreateDirRequest); + return writeU8U16StringMsg(dirNameLen, pDirName); +} + +bool +CFTMsgWriter::writeFileDirSizeRqst(unsigned short dirNameLen, char *pDirName) +{ + if (dirNameLen >= FT_FILENAME_SIZE) return false; + + m_pOutStream->writeU8(msgTypeFileDirSizeRequest); + return writeU8U16StringMsg(dirNameLen, pDirName); +} + +bool +CFTMsgWriter::writeFileRenameRqst(unsigned short oldNameLen, unsigned short newNameLen, + char *pOldName, char *pNewName) +{ + if ((oldNameLen >= FT_FILENAME_SIZE) || (newNameLen >= FT_FILENAME_SIZE)) return false; + + m_pOutStream->writeU8(msgTypeFileRenameRequest); + m_pOutStream->writeU8(0); + m_pOutStream->writeU16(oldNameLen); + m_pOutStream->writeU16(newNameLen); + m_pOutStream->writeBytes(pOldName, oldNameLen); + m_pOutStream->writeBytes(pNewName, newNameLen); + m_pOutStream->flush(); + + return true; +} + +bool +CFTMsgWriter::writeFileDeleteRqst(unsigned short nameLen, char *pName) +{ + if (nameLen >= FT_FILENAME_SIZE) return false; + + m_pOutStream->writeU8(msgTypeFileDeleteRequest); + return writeU8U16StringMsg(nameLen, pName); +} + +bool +CFTMsgWriter::writeU8U16StringMsg(unsigned short strLength, char *pString) +{ + m_pOutStream->writeU8(0); + m_pOutStream->writeU16(strLength); + m_pOutStream->writeBytes(pString, strLength); + m_pOutStream->flush(); + return true; +} diff --git a/common/rfb/CFTMsgWriter.h b/common/rfb/CFTMsgWriter.h new file mode 100644 index 00000000..e4cdc0bd --- /dev/null +++ b/common/rfb/CFTMsgWriter.h @@ -0,0 +1,67 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- CFTMsgWriter.h + +#ifndef __RFB_CFTMSGWRITER_H__ +#define __RFB_CFTMSGWRITER_H__ + +#include <rdr/types.h> +#include <rdr/OutStream.h> +#include <rfb/msgTypes.h> +#include <rfb/fttypes.h> + +namespace rfb { + class CFTMsgWriter + { + public: + CFTMsgWriter(rdr::OutStream *pOS); + ~CFTMsgWriter(); + + bool writeFileListRqst(unsigned short dirnameLen, char *pDirName, bool bDirOnly); + + bool writeFileDownloadCancel(unsigned short reasonLen, char *pReason); + bool writeFileDownloadRqst(unsigned short filenameLen, char *pFilename, + unsigned int position); + + bool writeFileUploadData(unsigned short dataSize, char *pData); + bool writeFileUploadData(unsigned int modTime); + bool writeFileUploadFailed(unsigned short reasonLen, char *pReason); + bool writeFileUploadRqst(unsigned short filenameLen, char *pFilename, + unsigned int position); + + bool writeFileCreateDirRqst(unsigned short dirNameLen, char *pDirName); + bool writeFileDirSizeRqst(unsigned short dirNameLen, char *pDirName); + + bool writeFileRenameRqst(unsigned short oldNameLen, unsigned short newNameLen, + char *pOldName, char *pNewName); + bool writeFileDeleteRqst(unsigned short nameLen, char *pName); + + private: + rdr::OutStream *m_pOutStream; + + bool writeU8U16StringMsg(unsigned short strLength, char *pString); + }; +} + +#endif // __RFB_CFTMSGWRITER_H__ diff --git a/common/rfb/CMsgHandler.cxx b/common/rfb/CMsgHandler.cxx new file mode 100644 index 00000000..bbc11763 --- /dev/null +++ b/common/rfb/CMsgHandler.cxx @@ -0,0 +1,103 @@ +/* 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/Exception.h> +#include <rfb/CMsgHandler.h> + +using namespace rfb; + +CMsgHandler::CMsgHandler() +{ +} + +CMsgHandler::~CMsgHandler() +{ +} + +void CMsgHandler::setDesktopSize(int width, int height) +{ + cp.width = width; + cp.height = height; +} + +void CMsgHandler::setCursor(int w, int h, const Point& hotspot, void* data, void* mask) +{ +} + +void CMsgHandler::setPixelFormat(const PixelFormat& pf) +{ + cp.setPF(pf); +} + +void CMsgHandler::setName(const char* name) +{ + cp.setName(name); +} + +void CMsgHandler::serverInit() +{ + throw Exception("CMsgHandler::serverInit called"); +} + +void CMsgHandler::framebufferUpdateStart() +{ +} + +void CMsgHandler::framebufferUpdateEnd() +{ +} + +void CMsgHandler::beginRect(const Rect& r, unsigned int encoding) +{ +} + +void CMsgHandler::endRect(const Rect& r, unsigned int encoding) +{ +} + + +void CMsgHandler::setColourMapEntries(int firstColour, int nColours, + rdr::U16* rgbs) +{ + throw Exception("CMsgHandler::setColourMapEntries called"); +} + +void CMsgHandler::bell() +{ +} + +void CMsgHandler::serverCutText(const char* str, int len) +{ +} + +void CMsgHandler::fillRect(const Rect& r, Pixel pix) +{ +} + +void CMsgHandler::imageRect(const Rect& r, void* pixels) +{ +} + +void CMsgHandler::copyRect(const Rect& r, int srcX, int srcY) +{ +} + +bool CMsgHandler::processFTMsg(int type) +{ + return false; +} + diff --git a/common/rfb/CMsgHandler.h b/common/rfb/CMsgHandler.h new file mode 100644 index 00000000..6c86df01 --- /dev/null +++ b/common/rfb/CMsgHandler.h @@ -0,0 +1,71 @@ +/* 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. + */ +// +// 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> + +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(), 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 setCursor(int width, int height, const Point& hotspot, + void* data, void* mask); + virtual void setPixelFormat(const PixelFormat& pf); + virtual void setName(const char* name); + virtual void serverInit(); + + virtual void framebufferUpdateStart(); + virtual void framebufferUpdateEnd(); + virtual void beginRect(const Rect& r, unsigned int encoding); + virtual void endRect(const Rect& r, unsigned int encoding); + + virtual void setColourMapEntries(int firstColour, int nColours, + rdr::U16* rgbs); + virtual void bell(); + virtual void serverCutText(const char* str, int len); + + virtual void fillRect(const Rect& r, Pixel pix); + virtual void imageRect(const Rect& r, void* pixels); + virtual void copyRect(const Rect& r, int srcX, int srcY); + + virtual bool processFTMsg(int type); + + ConnParams cp; + }; +} +#endif diff --git a/common/rfb/CMsgReader.cxx b/common/rfb/CMsgReader.cxx new file mode 100644 index 00000000..38547c0f --- /dev/null +++ b/common/rfb/CMsgReader.cxx @@ -0,0 +1,158 @@ +/* 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 <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_), + imageBuf(0), imageBufSize(0) +{ + for (unsigned int i = 0; i <= encodingMax; i++) { + decoders[i] = 0; + } +} + +CMsgReader::~CMsgReader() +{ + for (unsigned int i = 0; i <= encodingMax; i++) { + delete decoders[i]; + } + delete [] imageBuf; +} + +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); + int 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::readFramebufferUpdateStart() +{ + handler->framebufferUpdateStart(); +} + +void CMsgReader::readFramebufferUpdateEnd() +{ + handler->framebufferUpdateEnd(); +} + +void CMsgReader::readRect(const Rect& r, unsigned 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->beginRect(r, encoding); + + if (encoding == encodingCopyRect) { + readCopyRect(r); + } else { + if (!decoders[encoding]) { + decoders[encoding] = Decoder::createDecoder(encoding, this); + if (!decoders[encoding]) { + fprintf(stderr, "Unknown rect encoding %d\n", encoding); + throw Exception("Unknown rect encoding"); + } + } + decoders[encoding]->readRect(r, handler); + } + + handler->endRect(r, encoding); +} + +void CMsgReader::readCopyRect(const Rect& r) +{ + int srcX = is->readU16(); + int srcY = is->readU16(); + handler->copyRect(r, srcX, srcY); +} + +void CMsgReader::readSetCursor(int width, int height, const Point& hotspot) +{ + 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); + + is->readBytes(data.buf, data_len); + is->readBytes(mask.buf, mask_len); + + handler->setCursor(width, height, hotspot, data.buf, mask.buf); +} + +rdr::U8* CMsgReader::getImageBuf(int required, int requested, int* nPixels) +{ + int requiredBytes = required * (handler->cp.pf().bpp / 8); + int requestedBytes = requested * (handler->cp.pf().bpp / 8); + int size = requestedBytes; + if (size > imageBufIdealSize) size = imageBufIdealSize; + + if (size < requiredBytes) + size = requiredBytes; + + if (imageBufSize < size) { + imageBufSize = size; + delete [] imageBuf; + imageBuf = new rdr::U8[imageBufSize]; + } + if (nPixels) + *nPixels = imageBufSize / (handler->cp.pf().bpp / 8); + return imageBuf; +} + +int CMsgReader::bpp() +{ + return handler->cp.pf().bpp; +} diff --git a/common/rfb/CMsgReader.h b/common/rfb/CMsgReader.h new file mode 100644 index 00000000..7a611fc8 --- /dev/null +++ b/common/rfb/CMsgReader.h @@ -0,0 +1,73 @@ +/* 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. + */ +// +// 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/encodings.h> +#include <rfb/Decoder.h> + +namespace rdr { class InStream; } + +namespace rfb { + class CMsgHandler; + struct Rect; + + class CMsgReader { + public: + virtual ~CMsgReader(); + + virtual void readServerInit()=0; + + // readMsg() reads a message, calling the handler as appropriate. + virtual void readMsg()=0; + + rdr::InStream* getInStream() { return is; } + rdr::U8* getImageBuf(int required, int requested=0, int* nPixels=0); + int bpp(); + + int imageBufIdealSize; + + protected: + virtual void readSetColourMapEntries(); + virtual void readBell(); + virtual void readServerCutText(); + + virtual void readFramebufferUpdateStart(); + virtual void readFramebufferUpdateEnd(); + virtual void readRect(const Rect& r, unsigned int encoding); + + virtual void readCopyRect(const Rect& r); + + virtual void readSetCursor(int width, int height, const Point& hotspot); + + CMsgReader(CMsgHandler* handler, rdr::InStream* is); + + CMsgHandler* handler; + rdr::InStream* is; + Decoder* decoders[encodingMax+1]; + rdr::U8* imageBuf; + int imageBufSize; + }; +} +#endif diff --git a/common/rfb/CMsgReaderV3.cxx b/common/rfb/CMsgReaderV3.cxx new file mode 100644 index 00000000..d0cfc89f --- /dev/null +++ b/common/rfb/CMsgReaderV3.cxx @@ -0,0 +1,107 @@ +/* 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/PixelFormat.h> +#include <rfb/msgTypes.h> +#include <rfb/Exception.h> +#include <rdr/InStream.h> +#include <rfb/CMsgReaderV3.h> +#include <rfb/CMsgHandler.h> +#include <rfb/util.h> + +using namespace rfb; + +CMsgReaderV3::CMsgReaderV3(CMsgHandler* handler, rdr::InStream* is) + : CMsgReader(handler, is), nUpdateRectsLeft(0) +{ +} + +CMsgReaderV3::~CMsgReaderV3() +{ +} + +void CMsgReaderV3::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 CMsgReaderV3::readMsg() +{ + if (nUpdateRectsLeft == 0) { + + int type = is->readU8(); + switch (type) { + case msgTypeFramebufferUpdate: readFramebufferUpdate(); break; + case msgTypeSetColourMapEntries: readSetColourMapEntries(); break; + case msgTypeBell: readBell(); break; + case msgTypeServerCutText: readServerCutText(); break; + + case msgTypeFileListData: + case msgTypeFileDownloadData: + case msgTypeFileUploadCancel: + case msgTypeFileDownloadFailed: + case msgTypeFileDirSizeData: + case msgTypeFileLastRequestFailed: + handler->processFTMsg(type); 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(); + unsigned int encoding = is->readU32(); + + switch (encoding) { + case pseudoEncodingDesktopSize: + handler->setDesktopSize(w, h); + break; + case pseudoEncodingCursor: + readSetCursor(w, h, Point(x,y)); + break; + case pseudoEncodingLastRect: + nUpdateRectsLeft = 1; // this rectangle is the last one + break; + default: + readRect(Rect(x, y, x+w, y+h), encoding); + break; + }; + + nUpdateRectsLeft--; + if (nUpdateRectsLeft == 0) handler->framebufferUpdateEnd(); + } +} + +void CMsgReaderV3::readFramebufferUpdate() +{ + is->skip(1); + nUpdateRectsLeft = is->readU16(); + handler->framebufferUpdateStart(); +} diff --git a/common/rfb/CMsgReaderV3.h b/common/rfb/CMsgReaderV3.h new file mode 100644 index 00000000..689bb650 --- /dev/null +++ b/common/rfb/CMsgReaderV3.h @@ -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_CMSGREADERV3_H__ +#define __RFB_CMSGREADERV3_H__ + +#include <rfb/CMsgReader.h> + +namespace rfb { + class CMsgReaderV3 : public CMsgReader { + public: + CMsgReaderV3(CMsgHandler* handler, rdr::InStream* is); + virtual ~CMsgReaderV3(); + virtual void readServerInit(); + virtual void readMsg(); + private: + void readFramebufferUpdate(); + int nUpdateRectsLeft; + }; +} +#endif diff --git a/common/rfb/CMsgWriter.cxx b/common/rfb/CMsgWriter.cxx new file mode 100644 index 00000000..cdfb4e5f --- /dev/null +++ b/common/rfb/CMsgWriter.cxx @@ -0,0 +1,132 @@ +/* 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 <rdr/OutStream.h> +#include <rfb/msgTypes.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::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++] = pseudoEncodingCursor; + if (cp->supportsDesktopResize) + encodings[nEncodings++] = pseudoEncodingDesktopSize; + if (Decoder::supported(preferredEncoding)) { + encodings[nEncodings++] = preferredEncoding; + } + if (useCopyRect) { + encodings[nEncodings++] = encodingCopyRect; + } + for (int i = encodingMax; i >= 0; i--) { + if (i != preferredEncoding && Decoder::supported(i)) { + encodings[nEncodings++] = i; + } + } + encodings[nEncodings++] = pseudoEncodingLastRect; + if (cp->customCompressLevel && cp->compressLevel >= 0 && cp->compressLevel <= 9) + encodings[nEncodings++] = pseudoEncodingCompressLevel0 + cp->compressLevel; + if (!cp->noJpeg && cp->qualityLevel >= 1 && cp->qualityLevel <= 9) + encodings[nEncodings++] = pseudoEncodingQualityLevel0 + cp->qualityLevel; + + writeSetEncodings(nEncodings, encodings); +} + +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::keyEvent(rdr::U32 key, bool down) +{ + startMsg(msgTypeKeyEvent); + os->writeU8(down); + os->pad(2); + os->writeU32(key); + endMsg(); +} + + +void CMsgWriter::pointerEvent(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::clientCutText(const char* str, int len) +{ + startMsg(msgTypeClientCutText); + os->pad(3); + os->writeU32(len); + os->writeBytes(str, len); + endMsg(); +} diff --git a/common/rfb/CMsgWriter.h b/common/rfb/CMsgWriter.h new file mode 100644 index 00000000..19be8df9 --- /dev/null +++ b/common/rfb/CMsgWriter.h @@ -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. + */ +// +// CMsgWriter - class for writing RFB messages on the server side. +// + +#ifndef __RFB_CMSGWRITER_H__ +#define __RFB_CMSGWRITER_H__ + +#include <rfb/InputHandler.h> + +namespace rdr { class OutStream; } + +namespace rfb { + + class PixelFormat; + class ConnParams; + struct Rect; + + class CMsgWriter : public InputHandler { + public: + virtual ~CMsgWriter(); + + // CMsgWriter abstract interface methods + virtual void writeClientInit(bool shared)=0; + virtual void startMsg(int type)=0; + virtual void endMsg()=0; + + // CMsgWriter implemented methods + virtual void writeSetPixelFormat(const PixelFormat& pf); + virtual void writeSetEncodings(int nEncodings, rdr::U32* encodings); + virtual void writeSetEncodings(int preferredEncoding, bool useCopyRect); + virtual void writeFramebufferUpdateRequest(const Rect& r,bool incremental); + + // InputHandler implementation + virtual void keyEvent(rdr::U32 key, bool down); + virtual void pointerEvent(const Point& pos, int buttonMask); + virtual void clientCutText(const char* str, int len); + + ConnParams* getConnParams() { return cp; } + rdr::OutStream* getOutStream() { return os; } + + protected: + CMsgWriter(ConnParams* cp, rdr::OutStream* os); + + ConnParams* cp; + rdr::OutStream* os; + }; +} +#endif diff --git a/common/rfb/CMsgWriterV3.cxx b/common/rfb/CMsgWriterV3.cxx new file mode 100644 index 00000000..a9807952 --- /dev/null +++ b/common/rfb/CMsgWriterV3.cxx @@ -0,0 +1,49 @@ +/* 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/OutStream.h> +#include <rfb/msgTypes.h> +#include <rfb/Exception.h> +#include <rfb/ConnParams.h> +#include <rfb/CMsgWriterV3.h> + +using namespace rfb; + +CMsgWriterV3::CMsgWriterV3(ConnParams* cp, rdr::OutStream* os) + : CMsgWriter(cp, os) +{ +} + +CMsgWriterV3::~CMsgWriterV3() +{ +} + +void CMsgWriterV3::writeClientInit(bool shared) +{ + os->writeU8(shared); + endMsg(); +} + +void CMsgWriterV3::startMsg(int type) +{ + os->writeU8(type); +} + +void CMsgWriterV3::endMsg() +{ + os->flush(); +} diff --git a/common/rfb/CMsgWriterV3.h b/common/rfb/CMsgWriterV3.h new file mode 100644 index 00000000..0b2f9afe --- /dev/null +++ b/common/rfb/CMsgWriterV3.h @@ -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_CMSGWRITERV3_H__ +#define __RFB_CMSGWRITERV3_H__ + +#include <rfb/CMsgWriter.h> + +namespace rfb { + class CMsgWriterV3 : public CMsgWriter { + public: + CMsgWriterV3(ConnParams* cp, rdr::OutStream* os); + virtual ~CMsgWriterV3(); + + virtual void writeClientInit(bool shared); + virtual void startMsg(int type); + virtual void endMsg(); + + }; +} +#endif diff --git a/common/rfb/CSecurity.h b/common/rfb/CSecurity.h new file mode 100644 index 00000000..90a01d70 --- /dev/null +++ b/common/rfb/CSecurity.h @@ -0,0 +1,52 @@ +/* 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__ + +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; + }; +} +#endif diff --git a/common/rfb/CSecurityNone.h b/common/rfb/CSecurityNone.h new file mode 100644 index 00000000..54c10168 --- /dev/null +++ b/common/rfb/CSecurityNone.h @@ -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. + */ +// +// CSecurityNone.h +// + +#ifndef __CSECURITYNONE_H__ +#define __CSECURITYNONE_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 diff --git a/common/rfb/CSecurityVncAuth.cxx b/common/rfb/CSecurityVncAuth.cxx new file mode 100644 index 00000000..ba5a30b7 --- /dev/null +++ b/common/rfb/CSecurityVncAuth.cxx @@ -0,0 +1,74 @@ +/* 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/UserPasswdGetter.h> +#include <rfb/Password.h> +#include <rfb/CSecurityVncAuth.h> +#include <rfb/util.h> +extern "C" { +#include <rfb/d3des.h> +} + + +using namespace rfb; + +static const int vncAuthChallengeSize = 16; + + +CSecurityVncAuth::CSecurityVncAuth(UserPasswdGetter* upg_) + : upg(upg_) +{ +} + +CSecurityVncAuth::~CSecurityVncAuth() +{ +} + +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; + upg->getUserPasswd(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; +} diff --git a/common/rfb/CSecurityVncAuth.h b/common/rfb/CSecurityVncAuth.h new file mode 100644 index 00000000..8d38d878 --- /dev/null +++ b/common/rfb/CSecurityVncAuth.h @@ -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. + */ +#ifndef __RFB_CSECURITYVNCAUTH_H__ +#define __RFB_CSECURITYVNCAUTH_H__ + +#include <rfb/CSecurity.h> +#include <rfb/secTypes.h> + +namespace rfb { + + class UserPasswdGetter; + + class CSecurityVncAuth : public CSecurity { + public: + CSecurityVncAuth(UserPasswdGetter* pg); + virtual ~CSecurityVncAuth(); + virtual bool processMsg(CConnection* cc); + virtual int getType() const {return secTypeVncAuth;}; + virtual const char* description() const {return "No Encryption";} + private: + UserPasswdGetter* upg; + }; +} +#endif diff --git a/common/rfb/ColourCube.h b/common/rfb/ColourCube.h new file mode 100644 index 00000000..b83cbba8 --- /dev/null +++ b/common/rfb/ColourCube.h @@ -0,0 +1,96 @@ +/* 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. + */ +// +// ColourCube - structure to represent a colour cube. The colour cube consists +// of its dimensions (nRed x nGreen x nBlue) and a table mapping an (r,g,b) +// triple to a pixel value. +// +// A colour cube is used in two cases. The first is internally in a viewer +// when it cannot use a trueColour format, nor can it have exclusive access to +// a writable colour map. This is most notably the case for an X viewer +// wishing to use a PseudoColor X server's default colormap. +// +// The second use is on the server side when a client has asked for a colour +// map and the server is trueColour. Instead of setting an uneven trueColour +// format like bgr233, it can set the client's colour map up with a 6x6x6 +// colour cube. For this use the colour cube table has a null mapping, which +// makes it easy to perform the reverse lookup operation from pixel value to +// r,g,b values. + +#ifndef __RFB_COLOURCUBE_H__ +#define __RFB_COLOURCUBE_H__ + +#include <rfb/Pixel.h> +#include <rfb/ColourMap.h> + +namespace rfb { + + class ColourCube : public ColourMap { + public: + ColourCube(int nr, int ng, int nb, Pixel* table_=0) + : nRed(nr), nGreen(ng), nBlue(nb), table(table_), deleteTable(false) + { + if (!table) { + table = new Pixel[size()]; + deleteTable = true; + // set a null mapping by default + for (int i = 0; i < size(); i++) + table[i] = i; + } + } + + ColourCube() : deleteTable(false) {} + + virtual ~ColourCube() { + if (deleteTable) delete [] table; + } + + void set(int r, int g, int b, Pixel p) { + table[(r * nGreen + g) * nBlue + b] = p; + } + + Pixel lookup(int r, int g, int b) const { + return table[(r * nGreen + g) * nBlue + b]; + } + + int size() const { return nRed*nGreen*nBlue; } + int redMult() const { return nGreen*nBlue; } + int greenMult() const { return nBlue; } + int blueMult() const { return 1; } + + // ColourMap lookup() method. Note that this only works when the table has + // the default null mapping. + virtual void lookup(int i, int* r, int* g, int* b) { + if (i >= size()) return; + *b = i % nBlue; + i /= nBlue; + *g = i % nGreen; + *r = i / nGreen; + *r = (*r * 65535 + (nRed-1) / 2) / (nRed-1); + *g = (*g * 65535 + (nGreen-1) / 2) / (nGreen-1); + *b = (*b * 65535 + (nBlue-1) / 2) / (nBlue-1); + } + + int nRed; + int nGreen; + int nBlue; + Pixel* table; + bool deleteTable; + }; +} +#endif diff --git a/common/rfb/ColourMap.h b/common/rfb/ColourMap.h new file mode 100644 index 00000000..da6cb126 --- /dev/null +++ b/common/rfb/ColourMap.h @@ -0,0 +1,34 @@ +/* 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_COLOURMAP_H__ +#define __RFB_COLOURMAP_H__ +namespace rfb { + struct Colour { + Colour() : r(0), g(0), b(0) {} + Colour(int r_, int g_, int b_) : r(r_), g(g_), b(b_) {} + int r, g, b; + bool operator==(const Colour& c) const {return c.r == r && c.g == g && c.b == b;} + bool operator!=(const Colour& c) const {return !(c == *this);} + }; + + class ColourMap { + public: + virtual void lookup(int index, int* r, int* g, int* b)=0; + }; +} +#endif diff --git a/common/rfb/ComparingUpdateTracker.cxx b/common/rfb/ComparingUpdateTracker.cxx new file mode 100644 index 00000000..ce3d68ae --- /dev/null +++ b/common/rfb/ComparingUpdateTracker.cxx @@ -0,0 +1,137 @@ +/* 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 <vector> +#include <rdr/types.h> +#include <rfb/Exception.h> +#include <rfb/ComparingUpdateTracker.h> + +using namespace rfb; + +ComparingUpdateTracker::ComparingUpdateTracker(PixelBuffer* buffer) + : fb(buffer), oldFb(fb->getPF(), 0, 0), firstCompare(true) +{ + changed.assign_union(fb->getRect()); +} + +ComparingUpdateTracker::~ComparingUpdateTracker() +{ +} + + +#define BLOCK_SIZE 16 + +void ComparingUpdateTracker::compare() +{ + std::vector<Rect> rects; + std::vector<Rect>::iterator i; + + 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->getPixelsR(pos, &srcStride); + oldFb.imageRect(pos, srcData, srcStride); + } + firstCompare = false; + } else { + 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); + + Region to_check = changed.union_(copied); + to_check.get_rects(&rects); + + Region newChanged; + for (i = rects.begin(); i != rects.end(); i++) + compareRect(*i, &newChanged); + + copied.assign_subtract(newChanged); + changed = newChanged; + } +} + +void ComparingUpdateTracker::compareRect(const Rect& r, Region* newChanged) +{ + if (!r.enclosed_by(fb->getRect())) { + fprintf(stderr,"ComparingUpdateTracker: rect outside fb (%d,%d-%d,%d)\n", r.tl.x, r.tl.y, r.br.x, r.br.y); + return; + } + + int bytesPerPixel = fb->getPF().bpp/8; + int oldStride; + rdr::U8* oldData = oldFb.getPixelsRW(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->getPixelsR(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; + + for (int y = blockTop; y < blockBottom; y++) + { + if (memcmp(oldPtr, newPtr, blockWidthInBytes) != 0) + { + // A block has changed - copy the remainder to the oldFb + changedBlocks.push_back(Rect(blockLeft, blockTop, + blockRight, blockBottom)); + for (int y2 = y; y2 < blockBottom; y2++) + { + memcpy(oldPtr, newPtr, blockWidthInBytes); + newPtr += newStrideBytes; + oldPtr += oldStrideBytes; + } + break; + } + + newPtr += newStrideBytes; + oldPtr += oldStrideBytes; + } + + oldBlockPtr += blockWidthInBytes; + newBlockPtr += blockWidthInBytes; + } + + oldData += oldStrideBytes * BLOCK_SIZE; + } + + if (!changedBlocks.empty()) { + Region temp; + temp.setOrderedRects(changedBlocks); + newChanged->assign_union(temp); + } +} diff --git a/common/rfb/ComparingUpdateTracker.h b/common/rfb/ComparingUpdateTracker.h new file mode 100644 index 00000000..5d2e5edf --- /dev/null +++ b/common/rfb/ComparingUpdateTracker.h @@ -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_COMPARINGUPDATETRACKER_H__ +#define __RFB_COMPARINGUPDATETRACKER_H__ + +#include <rfb/UpdateTracker.h> + +namespace rfb { + + class ComparingUpdateTracker : public SimpleUpdateTracker { + public: + ComparingUpdateTracker(PixelBuffer* buffer); + ~ComparingUpdateTracker(); + + // compare() does the comparison and reduces its changed and copied regions + // as appropriate. + + virtual void compare(); + private: + void compareRect(const Rect& r, Region* newchanged); + PixelBuffer* fb; + ManagedPixelBuffer oldFb; + bool firstCompare; + }; + +} +#endif diff --git a/common/rfb/Configuration.cxx b/common/rfb/Configuration.cxx new file mode 100644 index 00000000..9ebc20a8 --- /dev/null +++ b/common/rfb/Configuration.cxx @@ -0,0 +1,466 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2004-2005 Cendio AB. 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.cxx + +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <assert.h> +#ifdef WIN32 +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#endif + +#include <rfb/util.h> +#include <rfb/Configuration.h> +#include <rfb/LogWriter.h> +#include <rfb/Exception.h> +#include <rfb/Threading.h> + +#ifdef __RFB_THREADING_IMPL +// On platforms that support Threading, we use Locks to make getData safe +#define LOCK_CONFIG Lock l(*configLock()) +rfb::Mutex* configLock_ = 0; +static rfb::Mutex* configLock() { + if (!configLock_) + configLock_ = new rfb::Mutex; + return configLock_; +} +#else +#define LOCK_CONFIG +#endif + +#include <rdr/HexOutStream.h> +#include <rdr/HexInStream.h> + +using namespace rfb; + +static LogWriter vlog("Config"); + + +// -=- The Global Configuration object +Configuration* Configuration::global_ = 0; +Configuration* Configuration::global() { + if (!global_) + global_ = new Configuration("Global"); + return global_; +} + + +// -=- Configuration implementation + +Configuration::Configuration(const char* name_, Configuration* attachToGroup) +: name(strDup(name_)), head(0), _next(0) { + if (attachToGroup) { + _next = attachToGroup->_next; + attachToGroup->_next = this; + } +} + +Configuration& Configuration::operator=(const Configuration& src) { + VoidParameter* current = head; + while (current) { + VoidParameter* srcParam = ((Configuration&)src).get(current->getName()); + if (srcParam) { + current->immutable = false; + CharArray value(srcParam->getValueStr()); + vlog.debug("operator=(%s, %s)", current->getName(), value.buf); + current->setParam(value.buf); + } + current = current->_next; + } + if (_next) + *_next=src; + return *this; +} + +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); + current->setHasBeenSet(); + 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(); + current->setHasBeenSet(); + 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); +} + + +// -=- VoidParameter + +VoidParameter::VoidParameter(const char* name_, const char* desc_, Configuration* conf) + : immutable(false), _hasBeenSet(false), name(name_), description(desc_) { + if (!conf) + conf = Configuration::global(); + _next = conf->head; + conf->head = this; +} + +VoidParameter::~VoidParameter() { +} + +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; +} + +void +VoidParameter::setHasBeenSet() { + _hasBeenSet = true; +} + +bool +VoidParameter::hasBeenSet() { + return _hasBeenSet; +} + +// -=- AliasParameter + +AliasParameter::AliasParameter(const char* name_, const char* desc_, + VoidParameter* param_, Configuration* conf) + : VoidParameter(name_, desc_, conf), 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, Configuration* conf) +: VoidParameter(name_, desc_, conf), 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; +} + +// -=- IntParameter + +IntParameter::IntParameter(const char* name_, const char* desc_, int v, + int minValue_, int maxValue_, Configuration* conf) + : VoidParameter(name_, desc_, conf), 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 = atoi(v); + 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, Configuration* conf) + : VoidParameter(name_, desc_, conf), 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); +} + +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); +} + +// -=- BinaryParameter + +BinaryParameter::BinaryParameter(const char* name_, const char* desc_, const void* v, int l, Configuration* conf) +: VoidParameter(name_, desc_, conf), 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); + } +} diff --git a/common/rfb/Configuration.h b/common/rfb/Configuration.h new file mode 100644 index 00000000..e3b85b83 --- /dev/null +++ b/common/rfb/Configuration.h @@ -0,0 +1,265 @@ +/* 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 rfb { + class VoidParameter; + struct ParameterIterator; + + // -=- Configuration + // Class used to access parameters. + + class Configuration { + public: + // - Create a new Configuration object + Configuration(const char* name, Configuration* attachToGroup=0); + + // - Return the buffer containing the Configuration's name + const char* getName() const { return name.buf; } + + // - Assignment operator. For every Parameter in this Configuration's + // group, get()s the corresponding source parameter and copies its + // content. + Configuration& operator=(const Configuration& src); + + // - 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); + + // - 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(); + + // - 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); } + + protected: + 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_; + }; + + // -=- VoidParameter + // Configuration parameter base-class. + + class VoidParameter { + public: + VoidParameter(const char* name_, const char* desc_, Configuration* conf=0); + 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(); + virtual void setHasBeenSet(); + bool hasBeenSet(); + + protected: + friend class Configuration; + friend struct ParameterIterator; + + VoidParameter* _next; + bool immutable; + bool _hasBeenSet; + const char* name; + const char* description; + }; + + class AliasParameter : public VoidParameter { + public: + AliasParameter(const char* name_, const char* desc_,VoidParameter* param_, Configuration* conf=0); + 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, Configuration* conf=0); + 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 IntParameter : public VoidParameter { + public: + IntParameter(const char* name_, const char* desc_, int v, + int minValue=INT_MIN, int maxValue=INT_MAX, Configuration* conf=0); + 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, Configuration* conf=0); + virtual ~StringParameter(); + virtual bool setParam(const char* value); + virtual char* getDefaultStr() const; + virtual char* getValueStr() 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, Configuration* conf=0); + 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 the Parameters in a Configuration group. The + // current Parameter is accessed via param, the current Configuration + // via config. The next() method moves on to the next Parameter. + + struct ParameterIterator { + ParameterIterator(Configuration* c) : config(c), param(c ? c->head : 0) {} + void next() { + param = param->_next; + while (!param) { + config = config->_next; + if (!config) break; + param = config->head; + } + } + Configuration* config; + VoidParameter* param; + }; + +}; + +#endif // __RFB_CONFIGURATION_H__ diff --git a/common/rfb/ConnParams.cxx b/common/rfb/ConnParams.cxx new file mode 100644 index 00000000..d4ae5894 --- /dev/null +++ b/common/rfb/ConnParams.cxx @@ -0,0 +1,125 @@ +/* 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/OutStream.h> +#include <rfb/Exception.h> +#include <rfb/encodings.h> +#include <rfb/Encoder.h> +#include <rfb/ConnParams.h> +#include <rfb/util.h> + +using namespace rfb; + +ConnParams::ConnParams() + : majorVersion(0), minorVersion(0), width(0), height(0), useCopyRect(false), + supportsLocalCursor(false), supportsLocalXCursor(false), supportsDesktopResize(true), + supportsLastRect(false), customCompressLevel(false), compressLevel(6), + noJpeg(false), qualityLevel(-1), + name_(0), nEncodings_(0), encodings_(0), + currentEncoding_(encodingRaw), verStrPos(0) +{ + setName(""); +} + +ConnParams::~ConnParams() +{ + delete [] name_; + delete [] encodings_; +} + +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::setEncodings(int nEncodings, const rdr::U32* encodings) +{ + if (nEncodings > nEncodings_) { + delete [] encodings_; + encodings_ = new rdr::U32[nEncodings]; + } + nEncodings_ = nEncodings; + useCopyRect = false; + supportsLocalCursor = false; + supportsDesktopResize = false; + supportsLocalXCursor = false; + supportsLastRect = false; + customCompressLevel = false; + compressLevel = -1; + noJpeg = true; + qualityLevel = -1; + currentEncoding_ = encodingRaw; + + for (int i = nEncodings-1; i >= 0; i--) { + encodings_[i] = encodings[i]; + + if (encodings[i] == encodingCopyRect) + useCopyRect = true; + else if (encodings[i] == pseudoEncodingCursor) + supportsLocalCursor = true; + else if (encodings[i] == pseudoEncodingXCursor) + supportsLocalXCursor = true; + else if (encodings[i] == pseudoEncodingDesktopSize) + supportsDesktopResize = true; + else if (encodings[i] == pseudoEncodingLastRect) + supportsLastRect = true; + else if (encodings[i] >= pseudoEncodingCompressLevel0 && + encodings[i] <= pseudoEncodingCompressLevel9) { + customCompressLevel = true; + compressLevel = encodings[i] - pseudoEncodingCompressLevel0; + } else if (encodings[i] >= pseudoEncodingQualityLevel0 && + encodings[i] <= pseudoEncodingQualityLevel9) { + noJpeg = false; + qualityLevel = encodings[i] - pseudoEncodingQualityLevel0; + } else if (encodings[i] <= encodingMax && Encoder::supported(encodings[i])) + currentEncoding_ = encodings[i]; + } +} diff --git a/common/rfb/ConnParams.h b/common/rfb/ConnParams.h new file mode 100644 index 00000000..47e6a5fb --- /dev/null +++ b/common/rfb/ConnParams.h @@ -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. + */ +// +// ConnParams - structure containing the connection parameters. +// + +#ifndef __RFB_CONNPARAMS_H__ +#define __RFB_CONNPARAMS_H__ + +#include <rdr/types.h> +#include <rfb/PixelFormat.h> + +namespace rdr { class InStream; } + +namespace rfb { + + 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) { + return majorVersion == major && minorVersion == minor; + } + bool beforeVersion(int major, int minor) { + return (majorVersion < major || + (majorVersion == major && minorVersion < minor)); + } + bool afterVersion(int major, int minor) { + return !beforeVersion(major,minor+1); + } + + int width; + int height; + + const PixelFormat& pf() { return pf_; } + void setPF(const PixelFormat& pf); + + const char* name() { return name_; } + void setName(const char* name); + + rdr::U32 currentEncoding() { return currentEncoding_; } + int nEncodings() { return nEncodings_; } + const rdr::U32* encodings() { return encodings_; } + void setEncodings(int nEncodings, const rdr::U32* encodings); + bool useCopyRect; + + bool supportsLocalCursor; + bool supportsLocalXCursor; + bool supportsDesktopResize; + bool supportsLastRect; + + bool customCompressLevel; + int compressLevel; + bool noJpeg; + int qualityLevel; + + private: + + PixelFormat pf_; + char* name_; + int nEncodings_; + rdr::U32* encodings_; + int currentEncoding_; + char verStr[13]; + int verStrPos; + }; +} +#endif diff --git a/common/rfb/Cursor.cxx b/common/rfb/Cursor.cxx new file mode 100644 index 00000000..c8dc341b --- /dev/null +++ b/common/rfb/Cursor.cxx @@ -0,0 +1,179 @@ +/* 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 <string.h> +#include <rfb/Cursor.h> +#include <rfb/LogWriter.h> + +using namespace rfb; + +static LogWriter vlog("Cursor"); + +void Cursor::setSize(int w, int h) { + int oldMaskLen = maskLen(); + ManagedPixelBuffer::setSize(w, h); + if (maskLen() > oldMaskLen) { + delete [] mask.buf; + mask.buf = new rdr::U8[maskLen()]; + } +} + +void Cursor::drawOutline(const Pixel& c) +{ + Cursor outlined; + + // Create a mirror of the existing cursor + outlined.setPF(getPF()); + outlined.setSize(width(), height()); + outlined.hotspot = hotspot; + + // Clear the mirror's background to the outline colour + outlined.fillRect(getRect(), c); + + // Blit the existing cursor, using its mask + outlined.maskRect(getRect(), data, mask.buf); + + // Now just adjust the mask to add the outline. The outline pixels + // will already be the right colour. :) + int maskBytesPerRow = (width() + 7) / 8; + for (int y = 0; y < height(); y++) { + for (int byte=0; byte<maskBytesPerRow; byte++) { + rdr::U8 m8 = mask.buf[y*maskBytesPerRow + byte]; + + // Handle above & below outline + if (y > 0) m8 |= mask.buf[(y-1)*maskBytesPerRow + byte]; + if (y < height()-1) m8 |= mask.buf[(y+1)*maskBytesPerRow + byte]; + + // Left outline + m8 |= mask.buf[y*maskBytesPerRow + byte] << 1; + if (byte < maskBytesPerRow-1) + m8 |= (mask.buf[y*maskBytesPerRow + byte + 1] >> 7) & 1; + + // Right outline + m8 |= mask.buf[y*maskBytesPerRow + byte] >> 1; + if (byte > 0) + m8 |= (mask.buf[y*maskBytesPerRow + byte - 1] << 7) & 128; + + outlined.mask.buf[y*maskBytesPerRow + byte] = m8; + } + } + + // Replace the existing cursor & mask with the new one + delete [] data; + delete [] mask.buf; + data = outlined.data; outlined.data = 0; + mask.buf = outlined.mask.buf; outlined.mask.buf = 0; +} + +rdr::U8* Cursor::getBitmap(Pixel* pix0, Pixel* pix1) +{ + bool gotPix0 = false; + bool gotPix1 = false; + *pix0 = *pix1 = 0; + rdr::U8Array source(maskLen()); + memset(source.buf, 0, maskLen()); + + int maskBytesPerRow = (width() + 7) / 8; + 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 (mask.buf[byte] & (1 << bit)) { + Pixel pix=0; + switch (getPF().bpp) { + case 8: pix = ((rdr::U8*) data)[y * width() + x]; break; + case 16: pix = ((rdr::U16*)data)[y * width() + x]; break; + case 32: pix = ((rdr::U32*)data)[y * width() + x]; break; + } + if (!gotPix0 || pix == *pix0) { + gotPix0 = true; + *pix0 = pix; + } else if (!gotPix1 || pix == *pix1) { + gotPix1 = true; + *pix1 = pix; + source.buf[byte] |= (1 << bit); + } else { + // not a bitmap + return 0; + } + } + } + } + return source.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 = getRect().intersect(Rect(hotspot.x, hotspot.y, + hotspot.x+1, hotspot.y+1)); + int maskBytesPerRow = (width() + 7) / 8; + int x, y; + for (y = 0; y < height(); y++) { + for (x = 0; x < width(); x++) { + int byte = y * maskBytesPerRow + x / 8; + int bit = 7 - x % 8; + if (mask.buf[byte] & (1 << bit)) { + 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; + } + } + } + + if (width() == busy.width() && height() == busy.height()) return; + + vlog.debug("cropping %dx%d to %dx%d", width(), height(), + busy.width(), busy.height()); + + // Copy the pixel data + int newDataLen = busy.area() * (getPF().bpp/8); + rdr::U8* newData = new rdr::U8[newDataLen]; + getImage(newData, busy); + + // Copy the mask + int newMaskBytesPerRow = (busy.width()+7)/8; + int newMaskLen = newMaskBytesPerRow * busy.height(); + rdr::U8* newMask = new rdr::U8[newMaskLen]; + memset(newMask, 0, newMaskLen); + for (y = 0; y < busy.height(); y++) { + int newByte, newBit; + for (x = 0; x < busy.width(); x++) { + int oldByte = (y+busy.tl.y) * maskBytesPerRow + (x+busy.tl.x) / 8; + int oldBit = 7 - (x+busy.tl.x) % 8; + newByte = y * newMaskBytesPerRow + x / 8; + newBit = 7 - x % 8; + if (mask.buf[oldByte] & (1 << oldBit)) + newMask[newByte] |= (1 << newBit); + } + } + + // Set the size and data to the new, cropped cursor. + setSize(busy.width(), busy.height()); + hotspot = hotspot.subtract(busy.tl); + delete [] data; + delete [] mask.buf; + datasize = newDataLen; + data = newData; + mask.buf = newMask; +} diff --git a/common/rfb/Cursor.h b/common/rfb/Cursor.h new file mode 100644 index 00000000..7d94d705 --- /dev/null +++ b/common/rfb/Cursor.h @@ -0,0 +1,56 @@ +/* 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. + */ +// +// 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 ManagedPixelBuffer { + public: + Cursor() {} + rdr::U8Array mask; + Point hotspot; + + int maskLen() { return (width() + 7) / 8 * height(); } + + // setSize() resizes the cursor. The contents of the data and mask are + // undefined after this call. + virtual void setSize(int w, int h); + + // drawOutline() adds an outline to the cursor in the given colour. + void drawOutline(const Pixel& c); + + // getBitmap() tests whether the cursor is monochrome, and if so returns a + // bitmap together with background and foreground colours. The size and + // layout of the bitmap are the same as the mask. + rdr::U8* getBitmap(Pixel* pix0, Pixel* pix1); + + // crop() crops the cursor down to the smallest possible size, based on the + // mask. + void crop(); + }; + +} +#endif diff --git a/common/rfb/Decoder.cxx b/common/rfb/Decoder.cxx new file mode 100644 index 00000000..b6e4fd5b --- /dev/null +++ b/common/rfb/Decoder.cxx @@ -0,0 +1,70 @@ +/* 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 <rfb/Exception.h> +#include <rfb/Decoder.h> +#include <rfb/RawDecoder.h> +#include <rfb/RREDecoder.h> +#include <rfb/HextileDecoder.h> +#include <rfb/ZRLEDecoder.h> +#include <rfb/TightDecoder.h> + +using namespace rfb; + +Decoder::~Decoder() +{ +} + +DecoderCreateFnType Decoder::createFns[encodingMax+1] = { 0 }; + +bool Decoder::supported(unsigned int encoding) +{ + return encoding <= encodingMax && createFns[encoding]; +} + +Decoder* Decoder::createDecoder(unsigned int encoding, CMsgReader* reader) +{ + if (encoding <= encodingMax && createFns[encoding]) + return (*createFns[encoding])(reader); + return 0; +} + +void Decoder::registerDecoder(unsigned int encoding, + DecoderCreateFnType createFn) +{ + if (encoding > encodingMax) + throw Exception("Decoder::registerDecoder: encoding out of range"); + + if (createFns[encoding]) + fprintf(stderr,"Replacing existing decoder for encoding %s (%d)\n", + encodingName(encoding), encoding); + createFns[encoding] = createFn; +} + +int DecoderInit::count = 0; + +DecoderInit::DecoderInit() +{ + if (count++ != 0) return; + + Decoder::registerDecoder(encodingRaw, RawDecoder::create); + Decoder::registerDecoder(encodingRRE, RREDecoder::create); + Decoder::registerDecoder(encodingHextile, HextileDecoder::create); + Decoder::registerDecoder(encodingZRLE, ZRLEDecoder::create); + Decoder::registerDecoder(encodingTight, TightDecoder::create); +} diff --git a/common/rfb/Decoder.h b/common/rfb/Decoder.h new file mode 100644 index 00000000..3fdba537 --- /dev/null +++ b/common/rfb/Decoder.h @@ -0,0 +1,52 @@ +/* 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_DECODER_H__ +#define __RFB_DECODER_H__ + +#include <rfb/Rect.h> +#include <rfb/encodings.h> + +namespace rfb { + class CMsgReader; + class CMsgHandler; + class Decoder; + typedef Decoder* (*DecoderCreateFnType)(CMsgReader*); + + class Decoder { + public: + virtual ~Decoder(); + virtual void readRect(const Rect& r, CMsgHandler* handler)=0; + + static bool supported(unsigned int encoding); + static Decoder* createDecoder(unsigned int encoding, CMsgReader* reader); + static void registerDecoder(unsigned int encoding, + DecoderCreateFnType createFn); + private: + static DecoderCreateFnType createFns[encodingMax+1]; + }; + + class DecoderInit { + static int count; + public: + DecoderInit(); + }; + + static DecoderInit decoderInitObj; +} + +#endif diff --git a/common/rfb/DirManager.h b/common/rfb/DirManager.h new file mode 100644 index 00000000..c820f648 --- /dev/null +++ b/common/rfb/DirManager.h @@ -0,0 +1,42 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- DirManager.cxx + +#ifndef __RFB_DIRMANAGER_H__ +#define __RFB_DIRMANAGER_H__ + +#include <rfb/FileInfo.h> + +namespace rfb { + class DirManager { + public: + virtual bool createDir(char *pFullPath) = 0; + virtual bool renameIt(char *pOldName, char *pNewName) = 0; + virtual bool deleteIt(char *pFullPath) = 0; + + virtual bool getDirInfo(char *pPath, FileInfo *pFileInfo, unsigned int dirOnly) = 0; + }; +} + +#endif // __RFB_DIRMANAGER_H__
\ No newline at end of file diff --git a/common/rfb/Encoder.cxx b/common/rfb/Encoder.cxx new file mode 100644 index 00000000..53cb1709 --- /dev/null +++ b/common/rfb/Encoder.cxx @@ -0,0 +1,77 @@ +/* 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 <rfb/Exception.h> +#include <rfb/Encoder.h> +#include <rfb/RawEncoder.h> +#include <rfb/RREEncoder.h> +#include <rfb/HextileEncoder.h> +#include <rfb/ZRLEEncoder.h> +#include <rfb/TightEncoder.h> + +using namespace rfb; + +Encoder::~Encoder() +{ +} + +EncoderCreateFnType Encoder::createFns[encodingMax+1] = { 0 }; + +bool Encoder::supported(unsigned int encoding) +{ + return encoding <= encodingMax && createFns[encoding]; +} + +Encoder* Encoder::createEncoder(unsigned int encoding, SMsgWriter* writer) +{ + if (encoding <= encodingMax && createFns[encoding]) + return (*createFns[encoding])(writer); + return 0; +} + +void Encoder::registerEncoder(unsigned int encoding, + EncoderCreateFnType createFn) +{ + if (encoding > encodingMax) + throw Exception("Encoder::registerEncoder: encoding out of range"); + + if (createFns[encoding]) + fprintf(stderr,"Replacing existing encoder for encoding %s (%d)\n", + encodingName(encoding), encoding); + createFns[encoding] = createFn; +} + +void Encoder::unregisterEncoder(unsigned int encoding) +{ + if (encoding > encodingMax) + throw Exception("Encoder::unregisterEncoder: encoding out of range"); + createFns[encoding] = 0; +} + +int EncoderInit::count = 0; + +EncoderInit::EncoderInit() +{ + if (count++ != 0) return; + + Encoder::registerEncoder(encodingRaw, RawEncoder::create); + Encoder::registerEncoder(encodingRRE, RREEncoder::create); + Encoder::registerEncoder(encodingHextile, HextileEncoder::create); + Encoder::registerEncoder(encodingZRLE, ZRLEEncoder::create); + Encoder::registerEncoder(encodingTight, TightEncoder::create); +} diff --git a/common/rfb/Encoder.h b/common/rfb/Encoder.h new file mode 100644 index 00000000..df50dd6d --- /dev/null +++ b/common/rfb/Encoder.h @@ -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. + */ +#ifndef __RFB_ENCODER_H__ +#define __RFB_ENCODER_H__ + +#include <rfb/Rect.h> +#include <rfb/encodings.h> + +namespace rfb { + class SMsgWriter; + class Encoder; + class ImageGetter; + typedef Encoder* (*EncoderCreateFnType)(SMsgWriter*); + + class Encoder { + public: + virtual ~Encoder(); + + virtual void setCompressLevel(int level) {}; + virtual void setQualityLevel(int level) {}; + virtual int getNumRects(const Rect &r) { return 1; } + + // writeRect() tries to write the given rectangle. If it is unable to + // write the whole rectangle it returns false and sets actual to the actual + // rectangle which was updated. + virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual)=0; + + static bool supported(unsigned int encoding); + static Encoder* createEncoder(unsigned int encoding, SMsgWriter* writer); + static void registerEncoder(unsigned int encoding, + EncoderCreateFnType createFn); + static void unregisterEncoder(unsigned int encoding); + private: + static EncoderCreateFnType createFns[encodingMax+1]; + }; + + class EncoderInit { + static int count; + public: + EncoderInit(); + }; + + static EncoderInit encoderInitObj; +} + +#endif diff --git a/common/rfb/Exception.h b/common/rfb/Exception.h new file mode 100644 index 00000000..7c2cbcaa --- /dev/null +++ b/common/rfb/Exception.h @@ -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. + */ +#ifndef __RFB_EXCEPTION_H__ +#define __RFB_EXCEPTION_H__ + +#include <rdr/Exception.h> + +namespace rfb { + typedef rdr::Exception Exception; + struct AuthFailureException : public Exception { + AuthFailureException(const char* s="Authentication failure") + : Exception(s) {} + }; + struct AuthCancelledException : public rfb::Exception { + AuthCancelledException(const char* s="Authentication cancelled") + : Exception(s) {} + }; + struct ConnFailedException : public Exception { + ConnFailedException(const char* s="Connection failed") : Exception(s) {} + }; +} +#endif diff --git a/common/rfb/FileInfo.cxx b/common/rfb/FileInfo.cxx new file mode 100644 index 00000000..e97e0adb --- /dev/null +++ b/common/rfb/FileInfo.cxx @@ -0,0 +1,244 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +#include <rfb/FileInfo.h> +#include <rfb/util.h> + +#ifdef _WIN32 +#define strcasecmp _stricmp +#endif + +using namespace rfb; + +// FIXME: Under Unix, file names are case-sensitive. + +int +CompareFileInfo(const void *F, const void *S) +{ + FILEINFO *pF = (FILEINFO *) F; + FILEINFO *pS = (FILEINFO *) S; + if (pF->info.flags == pS->info.flags) { + return strcasecmp(pF->name, pS->name); + } else { + if (pF->info.flags == FT_ATTR_DIR) return -1; + if (pS->info.flags == FT_ATTR_DIR) + return 1; + else + return strcasecmp(pF->name, pS->name); + } + + return 0; +} + +FileInfo::FileInfo() +{ + m_numEntries = 0; + m_pEntries = NULL; +} + +FileInfo::~FileInfo() +{ + free(); +} + +void +FileInfo::add(FileInfo *pFI) +{ + m_numEntries = pFI->getNumEntries(); + FILEINFO *pTemporary = new FILEINFO[m_numEntries]; + memcpy(pTemporary, pFI->getNameAt(0), m_numEntries * sizeof(FILEINFO)); + + m_pEntries = pTemporary; + pTemporary = NULL; +} + +void +FileInfo::add(FILEINFO *pFIStruct) +{ + add(pFIStruct->name, pFIStruct->info.size, pFIStruct->info.data, pFIStruct->info.flags); +} + +void +FileInfo::add(char *pName, unsigned int size, unsigned int data, unsigned int flags) +{ + FILEINFO *pTemporary = new FILEINFO[m_numEntries + 1]; + if (m_numEntries != 0) + memcpy(pTemporary, m_pEntries, m_numEntries * sizeof(FILEINFO)); + strcpy(pTemporary[m_numEntries].name, pName); + pTemporary[m_numEntries].info.size = size; + pTemporary[m_numEntries].info.data = data; + pTemporary[m_numEntries].info.flags = flags; + if (m_pEntries != NULL) { + delete [] m_pEntries; + m_pEntries = NULL; + } + m_pEntries = pTemporary; + pTemporary = NULL; + m_numEntries++; +} + +char * +FileInfo::getNameAt(unsigned int number) +{ + if ((number >= 0) && (number < m_numEntries)) { + return m_pEntries[number].name; + } + return NULL; +} + +bool +FileInfo::setNameAt(unsigned int number, char *pName) +{ + if ((number >= 0) && (number < m_numEntries)) { + strcpy(m_pEntries[number].name, pName); + return true; + } + return false; +} + +unsigned int +FileInfo::getSizeAt(unsigned int number) +{ + if ((number >= 0) && (number < m_numEntries)) { + return m_pEntries[number].info.size; + } + return 0; +} + +unsigned int +FileInfo::getDataAt(unsigned int number) +{ + if ((number >= 0) && (number < m_numEntries)) { + return m_pEntries[number].info.data; + } + return 0; +} + +unsigned int +FileInfo::getFlagsAt(unsigned int number) +{ + if ((number >= 0) && (number < m_numEntries)) { + return m_pEntries[number].info.flags; + } + return 0; +} + +FILEINFO * +FileInfo::getFullDataAt(unsigned int number) +{ + if ((number >= 0) && (number < m_numEntries)) { + return &m_pEntries[number]; + } + return NULL; +} + +bool +FileInfo::setSizeAt(unsigned int number, unsigned int value) +{ + if ((number >= 0) && (number < m_numEntries)) { + m_pEntries[number].info.size = value; + return true; + } + return false; +} + +bool +FileInfo::setDataAt(unsigned int number, unsigned int value) +{ + if ((number >= 0) && (number < m_numEntries)) { + m_pEntries[number].info.data = value; + return true; + } + return false; +} + +bool +FileInfo::setFlagsAt(unsigned int number, unsigned int value) +{ + if ((number >= 0) && (number < m_numEntries)) { + m_pEntries[number].info.flags = value; + return true; + } + return false; +} + +bool +FileInfo::deleteAt(unsigned int number) +{ + if ((number >= m_numEntries) || (number < 0)) return false; + + FILEINFO *pTemporary = new FILEINFO[m_numEntries - 1]; + + if (number == 0) { + memcpy(pTemporary, &m_pEntries[1], (m_numEntries - 1) * sizeof(FILEINFO)); + } else { + memcpy(pTemporary, m_pEntries, number * sizeof(FILEINFO)); + if (number != (m_numEntries - 1)) + memcpy(&pTemporary[number], &m_pEntries[number + 1], (m_numEntries - number - 1) * sizeof(FILEINFO)); + } + + if (m_pEntries != NULL) { + delete [] m_pEntries; + m_pEntries = NULL; + } + m_pEntries = pTemporary; + pTemporary = NULL; + m_numEntries--; + return true; +} + +unsigned int +FileInfo::getNumEntries() +{ + return m_numEntries; +} + +void +FileInfo::sort() +{ + qsort(m_pEntries, m_numEntries, sizeof(FILEINFO), CompareFileInfo); +} + +void +FileInfo::free() +{ + if (m_pEntries != NULL) { + delete [] m_pEntries; + m_pEntries = NULL; + } + m_numEntries = 0; +} + +unsigned int +FileInfo::getFilenamesSize() +{ + if (getNumEntries() == 0) return 0; + + unsigned int filenamesSize = 0; + + for (unsigned int i = 0; i < getNumEntries(); i++) { + filenamesSize += strlen(getNameAt(i)); + } + + return filenamesSize; +} diff --git a/common/rfb/FileInfo.h b/common/rfb/FileInfo.h new file mode 100644 index 00000000..270eeee9 --- /dev/null +++ b/common/rfb/FileInfo.h @@ -0,0 +1,74 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FileInfo. + +#ifndef __RFB_FILEINFO_H__ +#define __RFB_FILEINFO_H__ + +#include <stdlib.h> + +#include <rfb/fttypes.h> + +namespace rfb { + class FileInfo + { + public: + void add(FileInfo *pFI); + void add(FILEINFO *pFIStruct); + void add(char *pName, unsigned int size, unsigned int data, unsigned int flags); + + char *getNameAt(unsigned int number); + + bool setNameAt(unsigned int number, char *pName); + + unsigned int getSizeAt(unsigned int number); + unsigned int getDataAt(unsigned int number); + unsigned int getFlagsAt(unsigned int number); + + FILEINFO *getFullDataAt(unsigned int number); + + bool setSizeAt(unsigned int number, unsigned int value); + bool setDataAt(unsigned int number, unsigned int value); + bool setFlagsAt(unsigned int number, unsigned int value); + + bool deleteAt(unsigned int number); + + unsigned int getNumEntries(); + + unsigned int getFilenamesSize(); + + void sort(); + void free(); + + FileInfo(); + ~FileInfo(); + + private: + FILEINFO *m_pEntries; + unsigned int m_numEntries; + + }; +} + +#endif // __RFB_FILEINFO_H__ diff --git a/common/rfb/FileManager.cxx b/common/rfb/FileManager.cxx new file mode 100644 index 00000000..74cbd452 --- /dev/null +++ b/common/rfb/FileManager.cxx @@ -0,0 +1,81 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FileManager.cxx + +#include <rfb/FileManager.h> + +using namespace rfb; + +FileManager::FileManager() +{ + m_pFile = NULL; +} + +FileManager::~FileManager() +{ + close(); +} + +bool +FileManager::create(char *pFilename) +{ + if (m_pFile != NULL) return false; + + strcpy(m_szFilename, pFilename); + + m_pFile = fopen(m_szFilename, m_szMode); + + if (m_pFile == NULL) { + return false; + } else { + return true; + } +} + +bool +FileManager::close() +{ + if (m_pFile == NULL) return false; + + int result = fclose(m_pFile); + + if (result != 0) { + return false; + } else { + m_pFile = NULL; + return true; + } +} + +bool +FileManager::isCreated() +{ + if (m_pFile != NULL) return true; else return false; +} + +char * +FileManager::getFilename() +{ + return m_szFilename; +} diff --git a/common/rfb/FileManager.h b/common/rfb/FileManager.h new file mode 100644 index 00000000..4fd736f8 --- /dev/null +++ b/common/rfb/FileManager.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FileManager. + +#ifndef __RFB_FILEMANAGER_H__ +#define __RFB_FILEMANAGER_H__ + +#include <rfb/fttypes.h> + +namespace rfb { + class FileManager { + public: + FileManager(); + ~FileManager(); + + bool create(char *pFilename); + bool close(); + + bool isCreated(); + + char *getFilename(); + + protected: + FILE *m_pFile; + char m_szMode[4]; + char m_szFilename[FT_FILENAME_SIZE]; + }; +} +#endif // __RFB_FILEMANAGER_H__ diff --git a/common/rfb/FileReader.cxx b/common/rfb/FileReader.cxx new file mode 100644 index 00000000..a8cd2724 --- /dev/null +++ b/common/rfb/FileReader.cxx @@ -0,0 +1,51 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FileReader.cxx + +#include <rfb/FileReader.h> + +using namespace rfb; + +FileReader::FileReader() +{ + strcpy(m_szMode, "rb"); +} + +bool +FileReader::read(void *pBuf, unsigned int count, unsigned int *pBytesRead) +{ + if (!isCreated()) return false; + + *pBytesRead = fread(pBuf, 1, count, m_pFile); + + if (ferror(m_pFile)) return false; + + return true; +} + +unsigned int +FileReader::getTime() +{ + return 0; +} diff --git a/common/rfb/FileReader.h b/common/rfb/FileReader.h new file mode 100644 index 00000000..0c985d82 --- /dev/null +++ b/common/rfb/FileReader.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FileReader.h + +#ifndef __RFB_FILEREADER_H__ +#define __RFB_FILEREADER_H__ + +#include <rfb/FileManager.h> + +namespace rfb { + class FileReader : public FileManager { + public: + FileReader(); + + bool read(void *pBuf, unsigned int count, unsigned int *pBytesRead); + + unsigned int getTime(); + }; +} +#endif // __RFB_FILEREADER_H__ diff --git a/common/rfb/FileWriter.cxx b/common/rfb/FileWriter.cxx new file mode 100644 index 00000000..2bed5765 --- /dev/null +++ b/common/rfb/FileWriter.cxx @@ -0,0 +1,52 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FileWriter.cxx + +#include <rfb/FileWriter.h> + +using namespace rfb; + +FileWriter::FileWriter() +{ + strcpy(m_szMode, "wb"); +} + +bool +FileWriter::write(const void *pBuf, unsigned int count, unsigned int *pBytesWritten) +{ + if (!isCreated()) return false; + + unsigned int bytesWritten = fwrite(pBuf, 1, count, m_pFile); + + if (ferror(m_pFile)) return false; + + *pBytesWritten = bytesWritten; + return true; +} + +bool +FileWriter::setTime(unsigned int modTime) +{ + return false; +} diff --git a/common/rfb/FileWriter.h b/common/rfb/FileWriter.h new file mode 100644 index 00000000..73094a7e --- /dev/null +++ b/common/rfb/FileWriter.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- FileWriter.h + +#ifndef __RFB_FILEWRITER_H__ +#define __RFB_FILEWRITER_H__ + +#include <rfb/FileManager.h> + +namespace rfb { + class FileWriter : public FileManager { + public: + FileWriter(); + + bool write(const void *pBuf, unsigned int count, unsigned int *pBytesWritten); + bool setTime(unsigned int modTime); + }; +} + +#endif // __RFB_FILEWRITER_H__ diff --git a/common/rfb/HTTPServer.cxx b/common/rfb/HTTPServer.cxx new file mode 100644 index 00000000..e40d4802 --- /dev/null +++ b/common/rfb/HTTPServer.cxx @@ -0,0 +1,411 @@ +/* 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> + +#ifdef WIN32 +#define strcasecmp _stricmp +#endif + + +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: TightVNC/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::processSocketEvent(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::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; +} diff --git a/common/rfb/HTTPServer.h b/common/rfb/HTTPServer.h new file mode 100644 index 00000000..6412946a --- /dev/null +++ b/common/rfb/HTTPServer.h @@ -0,0 +1,110 @@ +/* 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 <list> + +#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); + + // processSocketEvent() + // The platform-specific side of the server implementation calls + // this method whenever data arrives on one of the active + // network sockets. + virtual void processSocketEvent(network::Socket* sock); + + // Check for socket timeouts + virtual int checkTimeouts(); + + + // 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); + + + // -=- 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 + diff --git a/common/rfb/HextileDecoder.cxx b/common/rfb/HextileDecoder.cxx new file mode 100644 index 00000000..e817c732 --- /dev/null +++ b/common/rfb/HextileDecoder.cxx @@ -0,0 +1,59 @@ +/* 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/CMsgReader.h> +#include <rfb/CMsgHandler.h> +#include <rfb/HextileDecoder.h> + +using namespace rfb; + +#define EXTRA_ARGS CMsgHandler* handler +#define FILL_RECT(r, p) handler->fillRect(r, p) +#define IMAGE_RECT(r, p) handler->imageRect(r, p) +#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 + +Decoder* HextileDecoder::create(CMsgReader* reader) +{ + return new HextileDecoder(reader); +} + +HextileDecoder::HextileDecoder(CMsgReader* reader_) : reader(reader_) +{ +} + +HextileDecoder::~HextileDecoder() +{ +} + +void HextileDecoder::readRect(const Rect& r, CMsgHandler* handler) +{ + rdr::InStream* is = reader->getInStream(); + rdr::U8* buf = reader->getImageBuf(16 * 16 * 4); + switch (reader->bpp()) { + case 8: hextileDecode8 (r, is, (rdr::U8*) buf, handler); break; + case 16: hextileDecode16(r, is, (rdr::U16*)buf, handler); break; + case 32: hextileDecode32(r, is, (rdr::U32*)buf, handler); break; + } +} diff --git a/common/rfb/HextileDecoder.h b/common/rfb/HextileDecoder.h new file mode 100644 index 00000000..e7dd3d58 --- /dev/null +++ b/common/rfb/HextileDecoder.h @@ -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_HEXTILEDECODER_H__ +#define __RFB_HEXTILEDECODER_H__ + +#include <rfb/Decoder.h> + +namespace rfb { + + class HextileDecoder : public Decoder { + public: + static Decoder* create(CMsgReader* reader); + virtual void readRect(const Rect& r, CMsgHandler* handler); + virtual ~HextileDecoder(); + private: + HextileDecoder(CMsgReader* reader); + CMsgReader* reader; + }; +} +#endif diff --git a/common/rfb/HextileEncoder.cxx b/common/rfb/HextileEncoder.cxx new file mode 100644 index 00000000..ba71d56d --- /dev/null +++ b/common/rfb/HextileEncoder.cxx @@ -0,0 +1,90 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2005 Constantin Kaplinsky. 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/ImageGetter.h> +#include <rfb/encodings.h> +#include <rfb/SMsgWriter.h> +#include <rfb/HextileEncoder.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 EXTRA_ARGS ImageGetter* ig +#define GET_IMAGE_INTO_BUF(r,buf) ig->getImage(buf, r); +#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 + +Encoder* HextileEncoder::create(SMsgWriter* writer) +{ + return new HextileEncoder(writer); +} + +HextileEncoder::HextileEncoder(SMsgWriter* writer_) : writer(writer_) +{ +} + +HextileEncoder::~HextileEncoder() +{ +} + +bool HextileEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual) +{ + writer->startRect(r, encodingHextile); + rdr::OutStream* os = writer->getOutStream(); + switch (writer->bpp()) { + case 8: + if (improvedHextile) { + hextileEncodeBetter8(r, os, ig); + } else { + hextileEncode8(r, os, ig); + } + break; + case 16: + if (improvedHextile) { + hextileEncodeBetter16(r, os, ig); + } else { + hextileEncode16(r, os, ig); + } + break; + case 32: + if (improvedHextile) { + hextileEncodeBetter32(r, os, ig); + } else { + hextileEncode32(r, os, ig); + } + break; + } + writer->endRect(); + return true; +} diff --git a/common/rfb/HextileEncoder.h b/common/rfb/HextileEncoder.h new file mode 100644 index 00000000..c78107a4 --- /dev/null +++ b/common/rfb/HextileEncoder.h @@ -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_HEXTILEENCODER_H__ +#define __RFB_HEXTILEENCODER_H__ + +#include <rfb/Encoder.h> + +namespace rfb { + + class HextileEncoder : public Encoder { + public: + static Encoder* create(SMsgWriter* writer); + virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual); + virtual ~HextileEncoder(); + private: + HextileEncoder(SMsgWriter* writer); + SMsgWriter* writer; + }; +} +#endif diff --git a/common/rfb/Hostname.h b/common/rfb/Hostname.h new file mode 100644 index 00000000..ebdf816f --- /dev/null +++ b/common/rfb/Hostname.h @@ -0,0 +1,55 @@ +/* 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 <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) { + CharArray portBuf; + CharArray hostBuf; + if (hi[0] == '[') { + if (!strSplit(&hi[1], ']', &hostBuf.buf, &portBuf.buf)) + throw rdr::Exception("unmatched [ in host"); + } else { + portBuf.buf = strDup(hi); + } + if (strSplit(portBuf.buf, ':', hostBuf.buf ? 0 : &hostBuf.buf, &portBuf.buf)) { + if (portBuf.buf[0] == ':') { + *port = atoi(&portBuf.buf[1]); + } else { + *port = atoi(portBuf.buf); + if (*port < 100) *port += basePort; + } + } else { + *port = basePort; + } + if (strlen(hostBuf.buf) == 0) + *host = strDup("localhost"); + else + *host = hostBuf.takeBuf(); + } + +}; + +#endif // __RFB_HOSTNAME_H__ diff --git a/common/rfb/ImageGetter.h b/common/rfb/ImageGetter.h new file mode 100644 index 00000000..290249f6 --- /dev/null +++ b/common/rfb/ImageGetter.h @@ -0,0 +1,30 @@ +/* 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_IMAGEGETTER_H__ +#define __RFB_IMAGEGETTER_H__ + +#include <rfb/Rect.h> + +namespace rfb { + class ImageGetter { + public: + virtual void getImage(void* imageBuf, + const Rect& r, int stride=0) = 0; + }; +} +#endif diff --git a/common/rfb/InputHandler.h b/common/rfb/InputHandler.h new file mode 100644 index 00000000..b5e5e879 --- /dev/null +++ b/common/rfb/InputHandler.h @@ -0,0 +1,40 @@ +/* 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> + +namespace rfb { + + class InputHandler { + public: + virtual ~InputHandler() {} + virtual void keyEvent(rdr::U32 key, bool down) {} + virtual void pointerEvent(const Point& pos, int buttonMask) {} + virtual void clientCutText(const char* str, int len) {} + }; + +} +#endif diff --git a/common/rfb/KeyRemapper.cxx b/common/rfb/KeyRemapper.cxx new file mode 100644 index 00000000..05f07632 --- /dev/null +++ b/common/rfb/KeyRemapper.cxx @@ -0,0 +1,84 @@ +/* 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 <rfb/KeyRemapper.h> +#include <rfb/Configuration.h> +#include <rfb/LogWriter.h> + +using namespace rfb; + +static LogWriter vlog("KeyRemapper"); + +KeyRemapper KeyRemapper::defInstance; + +#ifdef __RFB_THREADING_IMPL +static Mutex mappingLock; +#endif + +void KeyRemapper::setMapping(const char* m) { +#ifdef __RFB_THREADING_IMPL + Lock l(mappingLock); +#endif + 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", nextComma-m, m); + } + m = nextComma; + if (nextComma[0]) + m++; + } +} + +rdr::U32 KeyRemapper::remapKey(rdr::U32 key) const { +#ifdef __RFB_THREADING_IMPL + Lock l(mappingLock); +#endif + 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; + + diff --git a/common/rfb/KeyRemapper.h b/common/rfb/KeyRemapper.h new file mode 100644 index 00000000..a4b7aa01 --- /dev/null +++ b/common/rfb/KeyRemapper.h @@ -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. + */ + +#ifndef __RFB_KEYREMAPPER_H__ +#define __RFB_KEYREMAPPER_H__ + +#include <map> +#include <rdr/types.h> + +namespace rfb { + + class KeyRemapper { + public: + KeyRemapper(const char* m="") { setMapping(m); } + void setMapping(const char* m); + rdr::U32 remapKey(rdr::U32 key) const; + static KeyRemapper defInstance; + private: + std::map<rdr::U32,rdr::U32> mapping; + }; + +}; + +#endif // __RFB_KEYREMAPPER_H__ diff --git a/common/rfb/ListConnInfo.h b/common/rfb/ListConnInfo.h new file mode 100644 index 00000000..cabcbc79 --- /dev/null +++ b/common/rfb/ListConnInfo.h @@ -0,0 +1,122 @@ +/* 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__ + +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 status) { *si = status;} + + 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 + diff --git a/common/rfb/LogWriter.cxx b/common/rfb/LogWriter.cxx new file mode 100644 index 00000000..c6461d14 --- /dev/null +++ b/common/rfb/LogWriter.cxx @@ -0,0 +1,137 @@ +/* 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> +#ifdef WIN32 +#define strcasecmp _stricmp +#endif + +#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); +} diff --git a/common/rfb/LogWriter.h b/common/rfb/LogWriter.h new file mode 100644 index 00000000..124c58ec --- /dev/null +++ b/common/rfb/LogWriter.h @@ -0,0 +1,106 @@ +/* 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> + +// 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 name(const char* fmt, ...) { \ + 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); + + inline void write(int level, const char* format, ...) { + if (m_log && (level <= m_level)) { + va_list ap; + va_start(ap, format); + m_log->write(level, m_name, format, ap); + va_end(ap); + } + } + + DEF_LOGFUNCTION(error, 0) + DEF_LOGFUNCTION(status, 10) + DEF_LOGFUNCTION(info, 30) + DEF_LOGFUNCTION(debug, 100) + + // -=- 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__ diff --git a/common/rfb/Logger.cxx b/common/rfb/Logger.cxx new file mode 100644 index 00000000..52d33085 --- /dev/null +++ b/common/rfb/Logger.cxx @@ -0,0 +1,118 @@ +/* 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> +#ifdef WIN32 +#define strcasecmp _stricmp +#define vsnprintf _vsnprintf +#define HAVE_VSNPRINTF +#endif + +#include <rfb/Logger.h> +#include <rfb/LogWriter.h> +#include <rfb/util.h> +#include <rfb/Threading.h> + +using namespace rfb; + +#ifndef HAVE_VSNPRINTF +#ifdef __RFB_THREADING_IMPL +static Mutex fpLock; +#endif +static FILE* fp = 0; +int vsnprintf(char *str, size_t n, const char *format, va_list ap) +{ + str[0] = 0; + if (!fp) { + // Safely create a FILE* for /dev/null if there isn't already one +#ifdef __RFB_THREADING_IMPL + Lock l(fpLock); +#endif + if (!fp) + fp = fopen("/dev/null","w"); + if (!fp) return 0; + } + int len = vfprintf(fp, format, ap); + if (len <= 0) return 0; + + CharArray s(len+1); + vsprintf(s.buf, format, ap); + + int written = __rfbmin(len, (int)n-1); + memcpy(str, s.buf, written); + str[written] = 0; + return len; +} +#endif + + +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; + } +} + + diff --git a/common/rfb/Logger.h b/common/rfb/Logger.h new file mode 100644 index 00000000..e53764b7 --- /dev/null +++ b/common/rfb/Logger.h @@ -0,0 +1,70 @@ +/* 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. + +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); + + // -=- 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__ diff --git a/common/rfb/Logger_file.cxx b/common/rfb/Logger_file.cxx new file mode 100644 index 00000000..8a109e4a --- /dev/null +++ b/common/rfb/Logger_file.cxx @@ -0,0 +1,127 @@ +/* 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 <rfb/util.h> +#include <rfb/Logger_file.h> +#include <rfb/Threading.h> + +using namespace rfb; + + +// If threading is available then protect the write() operation +// from concurrent accesses +#ifdef __RFB_THREADING_IMPL +static Mutex logLock; +#endif + + +Logger_File::Logger_File(const char* loggerName) + : Logger(loggerName), indent(13), width(79), m_filename(0), m_file(0), + m_lastLogTime(0) +{ +} + +Logger_File::~Logger_File() +{ + closeFile(); +} + +void Logger_File::write(int level, const char *logname, const char *message) +{ +#ifdef __RFB_THREADING_IMPL + Lock l(logLock); +#endif + 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; + } + +#ifndef _WIN32_WCE + time_t current = time(0); + if (current != m_lastLogTime) { + m_lastLogTime = current; + fprintf(m_file, "\n%s", ctime(&m_lastLogTime)); + } +#endif + + 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,"\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; +} diff --git a/common/rfb/Logger_file.h b/common/rfb/Logger_file.h new file mode 100644 index 00000000..5e0c917b --- /dev/null +++ b/common/rfb/Logger_file.h @@ -0,0 +1,51 @@ +/* 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 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; + }; + + bool initFileLogger(const char* filename); +}; + +#endif diff --git a/common/rfb/Logger_stdio.cxx b/common/rfb/Logger_stdio.cxx new file mode 100644 index 00000000..581dcd5b --- /dev/null +++ b/common/rfb/Logger_stdio.cxx @@ -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; +} diff --git a/common/rfb/Logger_stdio.h b/common/rfb/Logger_stdio.h new file mode 100644 index 00000000..a1d17a0f --- /dev/null +++ b/common/rfb/Logger_stdio.h @@ -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 diff --git a/common/rfb/Makefile.in b/common/rfb/Makefile.in new file mode 100644 index 00000000..835e1882 --- /dev/null +++ b/common/rfb/Makefile.in @@ -0,0 +1,80 @@ + +CXXSRCS = \ + Blacklist.cxx \ + CConnection.cxx \ + CMsgHandler.cxx \ + CMsgReader.cxx \ + CMsgReaderV3.cxx \ + CMsgWriter.cxx \ + CMsgWriterV3.cxx \ + CSecurityVncAuth.cxx \ + ComparingUpdateTracker.cxx \ + Configuration.cxx \ + ConnParams.cxx \ + Cursor.cxx \ + Decoder.cxx \ + Encoder.cxx \ + FileInfo.cxx \ + FileManager.cxx \ + FileReader.cxx \ + FileWriter.cxx \ + HTTPServer.cxx \ + HextileDecoder.cxx \ + HextileEncoder.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 \ + SFTMsgReader.cxx \ + SFTMsgWriter.cxx \ + SFileTransfer.cxx \ + SFileTransferManager.cxx \ + SMsgHandler.cxx \ + SMsgReader.cxx \ + SMsgReaderV3.cxx \ + SMsgWriter.cxx \ + SMsgWriterV3.cxx \ + ServerCore.cxx \ + SSecurityFactoryStandard.cxx \ + SSecurityVncAuth.cxx \ + Timer.cxx \ + TightDecoder.cxx \ + TightEncoder.cxx \ + TightPalette.cxx \ + TransImageGetter.cxx \ + TransferQueue.cxx \ + UpdateTracker.cxx \ + VNCSConnectionST.cxx \ + VNCServerST.cxx \ + ZRLEEncoder.cxx \ + ZRLEDecoder.cxx \ + encodings.cxx \ + secTypes.cxx \ + util.cxx + +SRCS = d3des.c $(CXXSRCS) + +OBJS = d3des.o $(CXXSRCS:.cxx=.o) + +DIR_CPPFLAGS = -I$(top_srcdir) @ZLIB_INCLUDE@ @JPEG_INCLUDE@ @VSNPRINTF_DEFINE@ + +library = librfb.a + +all:: $(library) + +$(library): $(OBJS) + rm -f $(library) + $(AR) $(library) $(OBJS) + $(RANLIB) $(library) + +# followed by boilerplate.mk diff --git a/common/rfb/Password.cxx b/common/rfb/Password.cxx new file mode 100644 index 00000000..9127862d --- /dev/null +++ b/common/rfb/Password.cxx @@ -0,0 +1,77 @@ +/* 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(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); +} diff --git a/common/rfb/Password.h b/common/rfb/Password.h new file mode 100644 index 00000000..ab26903a --- /dev/null +++ b/common/rfb/Password.h @@ -0,0 +1,46 @@ +/* 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(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 diff --git a/common/rfb/Pixel.h b/common/rfb/Pixel.h new file mode 100644 index 00000000..4e9d1644 --- /dev/null +++ b/common/rfb/Pixel.h @@ -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 diff --git a/common/rfb/PixelBuffer.cxx b/common/rfb/PixelBuffer.cxx new file mode 100644 index 00000000..d093426f --- /dev/null +++ b/common/rfb/PixelBuffer.cxx @@ -0,0 +1,309 @@ +/* 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. + */ + +// -=- 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, ColourMap* cm) + : format(pf), width_(w), height_(h), colourmap(cm) {} +PixelBuffer::PixelBuffer() : width_(0), height_(0), colourmap(0) {} + +PixelBuffer::~PixelBuffer() {} + + +void PixelBuffer::setPF(const PixelFormat &pf) {format = pf;} +const PixelFormat& PixelBuffer::getPF() const {return format;} +ColourMap* PixelBuffer::getColourMap() const {return colourmap;} + + +void +PixelBuffer::getImage(void* imageBuf, const Rect& r, int outStride) { + int inStride; + const U8* data = getPixelsR(r, &inStride); + // We assume that the specified rectangle is pre-clipped to the buffer + int bytesPerPixel = format.bpp/8; + int inBytesPerRow = inStride * bytesPerPixel; + if (!outStride) outStride = r.width(); + int outBytesPerRow = outStride * bytesPerPixel; + int bytesPerMemCpy = r.width() * bytesPerPixel; + U8* imageBufPos = (U8*)imageBuf; + const U8* end = data + (inBytesPerRow * r.height()); + while (data < end) { + memcpy(imageBufPos, data, bytesPerMemCpy); + imageBufPos += outBytesPerRow; + data += inBytesPerRow; + } +} + +/* *** +Pixel PixelBuffer::getPixel(const Point& p) { + int stride; + Rect r = Rect(p.x, p.y, p.x+1, p.y+1); + switch(format.bpp) { + case 8: return *((rdr::U8*)getDataAt(r, &stride)); + case 16: return *((rdr::U16*)getDataAt(r, &stride)); + case 32: return *((rdr::U32*)getDataAt(r, &stride)); + default: return 0; + }; +} +*/ + + +FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h, + rdr::U8* data_, ColourMap* cm) + : PixelBuffer(pf, w, h, cm), data(data_) +{ +} + +FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {} + +FullFramePixelBuffer::~FullFramePixelBuffer() {} + + +int FullFramePixelBuffer::getStride() const { return width(); } + +rdr::U8* FullFramePixelBuffer::getPixelsRW(const Rect& r, int* stride) +{ + *stride = getStride(); + return &data[(r.tl.x + (r.tl.y * *stride)) * format.bpp/8]; +} + + +void FullFramePixelBuffer::fillRect(const Rect& r, Pixel pix) { + int stride; + U8* data = getPixelsRW(r, &stride); + int bytesPerPixel = getPF().bpp/8; + int bytesPerRow = bytesPerPixel * stride; + int bytesPerFill = bytesPerPixel * r.width(); + + U8* end = data + (bytesPerRow * r.height()); + while (data < end) { + switch (bytesPerPixel) { + case 1: + memset(data, pix, bytesPerFill); + break; + case 2: + { + U16* optr = (U16*)data; + U16* eol = optr + r.width(); + while (optr < eol) + *optr++ = pix; + } + break; + case 4: + { + U32* optr = (U32*)data; + U32* eol = optr + r.width(); + while (optr < eol) + *optr++ = pix; + } + break; + } + data += bytesPerRow; + } +} + +void FullFramePixelBuffer::imageRect(const Rect& r, const void* pixels, int srcStride) { + int bytesPerPixel = getPF().bpp/8; + int destStride; + U8* dest = getPixelsRW(r, &destStride); + int bytesPerDestRow = bytesPerPixel * destStride; + if (!srcStride) srcStride = r.width(); + int bytesPerSrcRow = bytesPerPixel * srcStride; + int bytesPerFill = bytesPerPixel * r.width(); + const U8* src = (const U8*)pixels; + U8* end = dest + (bytesPerDestRow * r.height()); + while (dest < end) { + memcpy(dest, src, bytesPerFill); + dest += bytesPerDestRow; + src += bytesPerSrcRow; + } +} + +void FullFramePixelBuffer::maskRect(const Rect& r, const void* pixels, const void* mask_) { + Rect cr = getRect().intersect(r); + if (cr.is_empty()) return; + int stride; + U8* data = getPixelsRW(cr, &stride); + U8* mask = (U8*) mask_; + int w = cr.width(); + int h = cr.height(); + int bpp = getPF().bpp; + int pixelStride = r.width(); + int maskStride = (r.width() + 7) / 8; + + Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y); + mask += offset.y * maskStride; + for (int y = 0; y < h; y++) { + int cy = offset.y + y; + for (int x = 0; x < w; x++) { + int cx = offset.x + x; + U8* byte = mask + (cx / 8); + int bit = 7 - cx % 8; + if ((*byte) & (1 << bit)) { + switch (bpp) { + case 8: + ((U8*)data)[y * stride + x] = ((U8*)pixels)[cy * pixelStride + cx]; + break; + case 16: + ((U16*)data)[y * stride + x] = ((U16*)pixels)[cy * pixelStride + cx]; + break; + case 32: + ((U32*)data)[y * stride + x] = ((U32*)pixels)[cy * pixelStride + cx]; + break; + } + } + } + mask += maskStride; + } +} + +void FullFramePixelBuffer::maskRect(const Rect& r, Pixel pixel, const void* mask_) { + Rect cr = getRect().intersect(r); + if (cr.is_empty()) return; + int stride; + U8* data = getPixelsRW(cr, &stride); + U8* mask = (U8*) mask_; + int w = cr.width(); + int h = cr.height(); + int bpp = getPF().bpp; + int maskStride = (r.width() + 7) / 8; + + Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y); + mask += offset.y * maskStride; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int cx = offset.x + x; + U8* byte = mask + (cx / 8); + int bit = 7 - cx % 8; + if ((*byte) & (1 << bit)) { + switch (bpp) { + case 8: + ((U8*)data)[y * stride + x] = pixel; + break; + case 16: + ((U16*)data)[y * stride + x] = pixel; + break; + case 32: + ((U32*)data)[y * stride + x] = pixel; + break; + } + } + } + mask += maskStride; + } +} + +void FullFramePixelBuffer::copyRect(const Rect &rect, const Point &move_by_delta) { + int stride; + U8* data = getPixelsRW(getRect(), &stride); + // We assume that the specified rectangle is pre-clipped to the buffer + unsigned int bytesPerPixel, bytesPerRow, bytesPerMemCpy; + Rect srect = rect.translate(move_by_delta.negate()); + bytesPerPixel = getPF().bpp/8; + bytesPerRow = stride * bytesPerPixel; + bytesPerMemCpy = rect.width() * bytesPerPixel; + if (move_by_delta.y <= 0) { + U8* dest = data + rect.tl.x*bytesPerPixel + rect.tl.y*bytesPerRow; + U8* src = data + srect.tl.x*bytesPerPixel + srect.tl.y*bytesPerRow; + for (int i=rect.tl.y; i<rect.br.y; i++) { + memmove(dest, src, bytesPerMemCpy); + dest += bytesPerRow; + src += bytesPerRow; + } + } else { + U8* dest = data + rect.tl.x*bytesPerPixel + (rect.br.y-1)*bytesPerRow; + U8* src = data + srect.tl.x*bytesPerPixel + (srect.br.y-1)*bytesPerRow; + for (int i=rect.tl.y; i<rect.br.y; i++) { + memmove(dest, src, bytesPerMemCpy); + dest -= bytesPerRow; + src -= bytesPerRow; + } + } +} + + +// -=- Managed pixel buffer class +// Automatically allocates enough space for the specified format & area + +ManagedPixelBuffer::ManagedPixelBuffer() + : datasize(0), own_colourmap(false) +{ + checkDataSize(); +}; + +ManagedPixelBuffer::ManagedPixelBuffer(const PixelFormat& pf, int w, int h) + : FullFramePixelBuffer(pf, w, h, 0, 0), datasize(0), own_colourmap(false) +{ + checkDataSize(); +}; + +ManagedPixelBuffer::~ManagedPixelBuffer() { + if (data) delete [] data; + if (colourmap && own_colourmap) delete colourmap; +}; + + +void +ManagedPixelBuffer::setPF(const PixelFormat &pf) { + format = pf; checkDataSize(); +}; +void +ManagedPixelBuffer::setSize(int w, int h) { + width_ = w; height_ = h; checkDataSize(); +}; + + +void +ManagedPixelBuffer::setColourMap(ColourMap* cm, bool own_cm) { + if (colourmap && own_colourmap) delete colourmap; + colourmap = cm; + own_colourmap = own_cm; +} + +inline void +ManagedPixelBuffer::checkDataSize() { + unsigned long new_datasize = width_ * height_ * (format.bpp/8); + if (datasize < new_datasize) { + vlog.debug("reallocating managed buffer (%dx%d)", width_, height_); + 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; + } + } +}; diff --git a/common/rfb/PixelBuffer.h b/common/rfb/PixelBuffer.h new file mode 100644 index 00000000..4a13923c --- /dev/null +++ b/common/rfb/PixelBuffer.h @@ -0,0 +1,172 @@ +/* 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. + */ + +// -=- 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/ImageGetter.h> +#include <rfb/PixelFormat.h> +#include <rfb/ColourMap.h> +#include <rfb/Rect.h> +#include <rfb/Pixel.h> + +namespace rfb { + + class Region; + + class PixelBuffer : public ImageGetter { + public: + PixelBuffer(const PixelFormat& pf, int width, int height, ColourMap* cm); + virtual ~PixelBuffer(); + + /////////////////////////////////////////////// + // Format / Layout + // + + // Set/get pixel format & colourmap + virtual void setPF(const PixelFormat &pf); + virtual const PixelFormat &getPF() const; + virtual ColourMap* getColourMap() const; + + // 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* getPixelsR(const Rect& r, int* stride) = 0; + + // Get pixel data for a given part of the buffer + // Data is copied into the supplied buffer, with the specified + // stride. + virtual void getImage(void* imageBuf, const Rect& r, int stride=0); + + // Get the data at (x,y) as a Pixel. + // VERY INEFFICIENT!!! + // *** Pixel getPixel(const Point& p); + + /////////////////////////////////////////////// + // 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& region) {} + + protected: + PixelBuffer(); + PixelFormat format; + int width_, height_; + ColourMap* colourmap; + }; + + // FullFramePixelBuffer + + class FullFramePixelBuffer : public PixelBuffer { + public: + FullFramePixelBuffer(const PixelFormat& pf, int width, int height, + rdr::U8* data_, ColourMap* cm); + virtual ~FullFramePixelBuffer(); + + // - Get the number of pixels per row in the actual pixel buffer data area + // This may in some cases NOT be the same as width(). + virtual int getStride() const; + + // Get a pointer to specified pixel data + virtual rdr::U8* getPixelsRW(const Rect& r, int* stride); + virtual const rdr::U8* getPixelsR(const Rect& r, int* stride) { + return getPixelsRW(r, stride); + } + + /////////////////////////////////////////////// + // Basic rendering operations + // These operations DO NOT clip to the pixelbuffer area, or trap overruns. + + // Fill a rectangle + virtual void fillRect(const Rect &dest, Pixel pix); + + // Copy pixel data to the buffer + virtual void imageRect(const Rect &dest, const void* pixels, int stride=0); + + // Copy pixel data from one PixelBuffer location to another + virtual void copyRect(const Rect &dest, const Point &move_by_delta); + + // Copy pixel data to the buffer through a mask + // pixels is a pointer to the pixel to be copied to r.tl. + // maskPos specifies the pixel offset in the mask to start from. + // mask_ is a pointer to the mask bits at (0,0). + // pStride and mStride are the strides of the pixel and mask buffers. + virtual void maskRect(const Rect& r, const void* pixels, const void* mask_); + + // pixel is the Pixel value to be used where mask_ is set + virtual void maskRect(const Rect& r, Pixel pixel, const void* mask_); + + // *** Should this be visible? + rdr::U8* data; + + protected: + FullFramePixelBuffer(); + }; + + // -=- 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); + + // Assign a colour map to the buffer + virtual void setColourMap(ColourMap* cm, bool own_cm); + + // 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; + bool own_colourmap; + void checkDataSize(); + }; + +}; + +#endif // __RFB_PIXEL_BUFFER_H__ diff --git a/common/rfb/PixelFormat.cxx b/common/rfb/PixelFormat.cxx new file mode 100644 index 00000000..74b68372 --- /dev/null +++ b/common/rfb/PixelFormat.cxx @@ -0,0 +1,239 @@ +/* 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 <string.h> +#include <rdr/InStream.h> +#include <rdr/OutStream.h> +#include <rfb/PixelFormat.h> +#include <rfb/util.h> + +#ifdef _WIN32 +#define strcasecmp _stricmp +#endif + +using namespace rfb; + +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), bigEndian(e), trueColour(t), + redMax(rm), greenMax(gm), blueMax(bm), + redShift(rs), greenShift(gs), blueShift(bs) +{ +} + +PixelFormat::PixelFormat() + : bpp(8), depth(8), bigEndian(false), trueColour(true), + redMax(7), greenMax(7), blueMax(3), + redShift(0), greenShift(3), blueShift(6) +{ +} + +bool PixelFormat::equal(const PixelFormat& other) const +{ + return (bpp == other.bpp && + depth == other.depth && + (bigEndian == other.bigEndian || bpp == 8) && + trueColour == other.trueColour && + (!trueColour || (redMax == other.redMax && + greenMax == other.greenMax && + blueMax == other.blueMax && + redShift == other.redShift && + greenShift == other.greenShift && + blueShift == other.blueShift))); +} + +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); +} + +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); +} + +Pixel PixelFormat::pixelFromRGB(rdr::U16 red, rdr::U16 green, rdr::U16 blue, + ColourMap* cm) const +{ + if (trueColour) { + rdr::U32 r = ((rdr::U32)red * redMax + 32767) / 65535; + rdr::U32 g = ((rdr::U32)green * greenMax + 32767) / 65535; + rdr::U32 b = ((rdr::U32)blue * blueMax + 32767) / 65535; + + return (r << redShift) | (g << greenShift) | (b << blueShift); + } else if (cm) { + // Try to find the closest pixel by Cartesian distance + int colours = 1 << depth; + int diff = 256 * 256 * 4; + int col = 0; + for (int i=0; i<colours; i++) { + int r, g, b; + cm->lookup(i, &r, &g, &b); + int rd = (r-red) >> 8; + int gd = (g-green) >> 8; + int bd = (b-blue) >> 8; + int d = rd*rd + gd*gd + bd*bd; + if (d < diff) { + col = i; + diff = d; + } + } + return col; + } + // XXX just return 0 for colour map? + return 0; +} + + +void PixelFormat::rgbFromPixel(Pixel p, ColourMap* cm, Colour* rgb) const +{ + if (trueColour) { + rgb->r = (((p >> redShift ) & redMax ) * 65535 + redMax /2) / redMax; + rgb->g = (((p >> greenShift) & greenMax) * 65535 + greenMax/2) / greenMax; + rgb->b = (((p >> blueShift ) & blueMax ) * 65535 + blueMax /2) / blueMax; + } else { + cm->lookup(p, &rgb->r, &rgb->g, &rgb->b); + } +} + + +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; + } + return true; +} diff --git a/common/rfb/PixelFormat.h b/common/rfb/PixelFormat.h new file mode 100644 index 00000000..111c38cb --- /dev/null +++ b/common/rfb/PixelFormat.h @@ -0,0 +1,58 @@ +/* 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. + */ +// +// PixelFormat - structure to represent a pixel format. Also has useful +// methods for reading & writing to streams, etc. +// + +#ifndef __RFB_PIXELFORMAT_H__ +#define __RFB_PIXELFORMAT_H__ + +#include <rfb/Pixel.h> +#include <rfb/ColourMap.h> + +namespace rdr { class InStream; class OutStream; } + +namespace rfb { + + class PixelFormat { + public: + PixelFormat(int b, int d, bool e, bool t, + int rm=0, int gm=0, int bm=0, int rs=0, int gs=0, int bs=0); + PixelFormat(); + bool equal(const PixelFormat& other) const; + void read(rdr::InStream* is); + void write(rdr::OutStream* os) const; + Pixel pixelFromRGB(rdr::U16 red, rdr::U16 green, rdr::U16 blue, ColourMap* cm=0) const; + void rgbFromPixel(Pixel pix, ColourMap* cm, Colour* rgb) const; + void print(char* str, int len) const; + bool parse(const char* str); + + int bpp; + int depth; + bool bigEndian; + bool trueColour; + int redMax; + int greenMax; + int blueMax; + int redShift; + int greenShift; + int blueShift; + }; +} +#endif diff --git a/common/rfb/RREDecoder.cxx b/common/rfb/RREDecoder.cxx new file mode 100644 index 00000000..da56ee7c --- /dev/null +++ b/common/rfb/RREDecoder.cxx @@ -0,0 +1,58 @@ +/* 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/CMsgReader.h> +#include <rfb/CMsgHandler.h> +#include <rfb/RREDecoder.h> + +using namespace rfb; + +#define EXTRA_ARGS CMsgHandler* handler +#define FILL_RECT(r, p) handler->fillRect(r, p) +#define IMAGE_RECT(r, p) handler->imageRect(r, p) +#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 + +Decoder* RREDecoder::create(CMsgReader* reader) +{ + return new RREDecoder(reader); +} + +RREDecoder::RREDecoder(CMsgReader* reader_) : reader(reader_) +{ +} + +RREDecoder::~RREDecoder() +{ +} + +void RREDecoder::readRect(const Rect& r, CMsgHandler* handler) +{ + rdr::InStream* is = reader->getInStream(); + switch (reader->bpp()) { + case 8: rreDecode8 (r, is, handler); break; + case 16: rreDecode16(r, is, handler); break; + case 32: rreDecode32(r, is, handler); break; + } +} diff --git a/common/rfb/RREDecoder.h b/common/rfb/RREDecoder.h new file mode 100644 index 00000000..2309f754 --- /dev/null +++ b/common/rfb/RREDecoder.h @@ -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_RREDECODER_H__ +#define __RFB_RREDECODER_H__ + +#include <rfb/Decoder.h> + +namespace rfb { + + class RREDecoder : public Decoder { + public: + static Decoder* create(CMsgReader* reader); + virtual void readRect(const Rect& r, CMsgHandler* handler); + virtual ~RREDecoder(); + private: + RREDecoder(CMsgReader* reader); + CMsgReader* reader; + }; +} +#endif diff --git a/common/rfb/RREEncoder.cxx b/common/rfb/RREEncoder.cxx new file mode 100644 index 00000000..b000e9d3 --- /dev/null +++ b/common/rfb/RREEncoder.cxx @@ -0,0 +1,75 @@ +/* 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/OutStream.h> +#include <rfb/ImageGetter.h> +#include <rfb/encodings.h> +#include <rfb/SMsgWriter.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 + +Encoder* RREEncoder::create(SMsgWriter* writer) +{ + return new RREEncoder(writer); +} + +RREEncoder::RREEncoder(SMsgWriter* writer_) : writer(writer_) +{ +} + +RREEncoder::~RREEncoder() +{ +} + +bool RREEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual) +{ + int w = r.width(); + int h = r.height(); + rdr::U8* imageBuf = writer->getImageBuf(w*h); + ig->getImage(imageBuf, r); + + mos.clear(); + + int nSubrects = -1; + switch (writer->bpp()) { + case 8: nSubrects = rreEncode8(imageBuf, w, h, &mos); break; + case 16: nSubrects = rreEncode16(imageBuf, w, h, &mos); break; + case 32: nSubrects = rreEncode32(imageBuf, w, h, &mos); break; + } + + if (nSubrects < 0) { + return writer->writeRect(r, encodingRaw, ig, actual); + } + + writer->startRect(r, encodingRRE); + rdr::OutStream* os = writer->getOutStream(); + os->writeU32(nSubrects); + os->writeBytes(mos.data(), mos.length()); + writer->endRect(); + return true; +} diff --git a/common/rfb/RREEncoder.h b/common/rfb/RREEncoder.h new file mode 100644 index 00000000..1281410d --- /dev/null +++ b/common/rfb/RREEncoder.h @@ -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. + */ +#ifndef __RFB_RREENCODER_H__ +#define __RFB_RREENCODER_H__ + +#include <rdr/MemOutStream.h> +#include <rfb/Encoder.h> + +namespace rfb { + + class RREEncoder : public Encoder { + public: + static Encoder* create(SMsgWriter* writer); + virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual); + virtual ~RREEncoder(); + private: + RREEncoder(SMsgWriter* writer); + SMsgWriter* writer; + rdr::MemOutStream mos; + }; +} +#endif diff --git a/common/rfb/RawDecoder.cxx b/common/rfb/RawDecoder.cxx new file mode 100644 index 00000000..57cb37bc --- /dev/null +++ b/common/rfb/RawDecoder.cxx @@ -0,0 +1,55 @@ +/* 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 <rfb/CMsgReader.h> +#include <rfb/CMsgHandler.h> +#include <rfb/RawDecoder.h> + +using namespace rfb; + +Decoder* RawDecoder::create(CMsgReader* reader) +{ + return new RawDecoder(reader); +} + +RawDecoder::RawDecoder(CMsgReader* reader_) : reader(reader_) +{ +} + +RawDecoder::~RawDecoder() +{ +} + +void RawDecoder::readRect(const Rect& r, CMsgHandler* handler) +{ + int x = r.tl.x; + int y = r.tl.y; + int w = r.width(); + int h = r.height(); + int nPixels; + rdr::U8* imageBuf = reader->getImageBuf(w, w*h, &nPixels); + int bytesPerRow = w * (reader->bpp() / 8); + while (h > 0) { + int nRows = nPixels / w; + if (nRows > h) nRows = h; + reader->getInStream()->readBytes(imageBuf, nRows * bytesPerRow); + handler->imageRect(Rect(x, y, x+w, y+nRows), imageBuf); + h -= nRows; + y += nRows; + } +} diff --git a/common/rfb/RawDecoder.h b/common/rfb/RawDecoder.h new file mode 100644 index 00000000..9fdbb220 --- /dev/null +++ b/common/rfb/RawDecoder.h @@ -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: + static Decoder* create(CMsgReader* reader); + virtual void readRect(const Rect& r, CMsgHandler* handler); + virtual ~RawDecoder(); + private: + RawDecoder(CMsgReader* reader); + CMsgReader* reader; + }; +} +#endif diff --git a/common/rfb/RawEncoder.cxx b/common/rfb/RawEncoder.cxx new file mode 100644 index 00000000..a2545b61 --- /dev/null +++ b/common/rfb/RawEncoder.cxx @@ -0,0 +1,59 @@ +/* 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/OutStream.h> +#include <rfb/ImageGetter.h> +#include <rfb/encodings.h> +#include <rfb/SMsgWriter.h> +#include <rfb/RawEncoder.h> + +using namespace rfb; + +Encoder* RawEncoder::create(SMsgWriter* writer) +{ + return new RawEncoder(writer); +} + +RawEncoder::RawEncoder(SMsgWriter* writer_) : writer(writer_) +{ +} + +RawEncoder::~RawEncoder() +{ +} + +bool RawEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual) +{ + int x = r.tl.x; + int y = r.tl.y; + int w = r.width(); + int h = r.height(); + int nPixels; + rdr::U8* imageBuf = writer->getImageBuf(w, w*h, &nPixels); + int bytesPerRow = w * (writer->bpp() / 8); + writer->startRect(r, encodingRaw); + while (h > 0) { + int nRows = nPixels / w; + if (nRows > h) nRows = h; + ig->getImage(imageBuf, Rect(x, y, x+w, y+nRows)); + writer->getOutStream()->writeBytes(imageBuf, nRows * bytesPerRow); + h -= nRows; + y += nRows; + } + writer->endRect(); + return true; +} diff --git a/common/rfb/RawEncoder.h b/common/rfb/RawEncoder.h new file mode 100644 index 00000000..1b9ad929 --- /dev/null +++ b/common/rfb/RawEncoder.h @@ -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_RAWENCODER_H__ +#define __RFB_RAWENCODER_H__ + +#include <rfb/Encoder.h> + +namespace rfb { + + class RawEncoder : public Encoder { + public: + static Encoder* create(SMsgWriter* writer); + virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual); + virtual ~RawEncoder(); + private: + RawEncoder(SMsgWriter* writer); + SMsgWriter* writer; + }; +} +#endif diff --git a/common/rfb/Rect.h b/common/rfb/Rect.h new file mode 100644 index 00000000..52e92b57 --- /dev/null +++ b/common/rfb/Rect.h @@ -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 unsigned 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__ diff --git a/common/rfb/Region.cxx b/common/rfb/Region.cxx new file mode 100644 index 00000000..7965a6c4 --- /dev/null +++ b/common/rfb/Region.cxx @@ -0,0 +1,248 @@ +/* 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 <Xregion/Xregion.h> +#include <Xregion/region.h> +#include <assert.h> +#include <stdio.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::copyFrom(const rfb::Region& r) { + XUnionRegion(r.xrgn, r.xrgn, xrgn); +} + +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, int maxArea) 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) { + int y = xrgn->rects[i].y1; + int h = maxArea / (xrgn->rects[i].x2 - xrgn->rects[i].x1); + if (!h) h = xrgn->rects[i].y2 - y; + do { + if (h > xrgn->rects[i].y2 - y) + h = xrgn->rects[i].y2 - y; + Rect r(xrgn->rects[i].x1, y, xrgn->rects[i].x2, y+h); + rects->push_back(r); + y += h; + } while (y < xrgn->rects[i].y2); + 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); + } +} diff --git a/common/rfb/Region.h b/common/rfb/Region.h new file mode 100644 index 00000000..93375569 --- /dev/null +++ b/common/rfb/Region.h @@ -0,0 +1,84 @@ +/* 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 copyFrom(const Region& r); + + 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, int maxArea=0) const; + Rect get_bounding_rect() const; + + void debug_print(const char *prefix) const; + + protected: + + struct _XRegion* xrgn; + }; + +}; + +#endif // __RFB_REGION_INCLUDED__ diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx new file mode 100644 index 00000000..1422b546 --- /dev/null +++ b/common/rfb/SConnection.cxx @@ -0,0 +1,322 @@ +/* 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 <string.h> +#include <rfb/Exception.h> +#include <rfb/secTypes.h> +#include <rfb/SMsgReaderV3.h> +#include <rfb/SMsgWriterV3.h> +#include <rfb/SConnection.h> +#include <rfb/ServerCore.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::AccessDefault = 0x03ff; +const SConnection::AccessRights SConnection::AccessNoQuery = 0x0400; +const SConnection::AccessRights SConnection::AccessFull = 0xffff; + + +SConnection::SConnection(SSecurityFactory* secFact, bool reverseConnection_) + : readyForSetColourMapEntries(false), + is(0), os(0), reader_(0), writer_(0), + security(0), securityFactory(secFact), state_(RFBSTATE_UNINITIALISED), + reverseConnection(reverseConnection_) +{ + defaultMajorVersion = 3; + defaultMinorVersion = 8; + if (rfb::Server::protocol3_3) + defaultMinorVersion = 3; + + cp.setVersion(defaultMajorVersion, defaultMinorVersion); +} + +SConnection::~SConnection() +{ + if (security) security->destroy(); + deleteReaderAndWriter(); +} + +void SConnection::deleteReaderAndWriter() +{ + 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 + char msg[256]; + sprintf(msg,"Error: client needs protocol version %d.%d, server has %d.%d", + cp.majorVersion, cp.minorVersion, + defaultMajorVersion, defaultMinorVersion); + throwConnFailedException(msg); + } + + 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; + securityFactory->getSecTypes(&secTypes, reverseConnection); + + 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()) { + char msg[256]; + sprintf(msg,"No supported security type for %d.%d client", + cp.majorVersion, cp.minorVersion); + throwConnFailedException(msg); + } + + os->writeU32(*i); + if (*i == secTypeNone) os->flush(); + state_ = RFBSTATE_SECURITY; + security = securityFactory->getSSecurity(*i, reverseConnection); + 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(); + + // Verify that the requested security type should be offered + std::list<rdr::U8> secTypes; + std::list<rdr::U8>::iterator i; + securityFactory->getSecTypes(&secTypes, reverseConnection); + 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; + security = securityFactory->getSSecurity(secType, reverseConnection); + } catch (rdr::Exception& e) { + throwConnFailedException(e.str()); + } + + processSecurityMsg(); +} + +void SConnection::processSecurityMsg() +{ + vlog.debug("processing security message"); + try { + bool done = security->processMsg(this); + if (done) { + state_ = RFBSTATE_QUERYING; + queryConnection(security->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* msg) +{ + vlog.info(msg); + if (state_ == RFBSTATE_PROTOCOL_VERSION) { + if (cp.majorVersion == 3 && cp.minorVersion == 3) { + os->writeU32(0); + os->writeString(msg); + os->flush(); + } else { + os->writeU8(0); + os->writeString(msg); + os->flush(); + } + } + state_ = RFBSTATE_INVALID; + throw ConnFailedException(msg); +} + +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::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 (!reason) reason = "Authentication failure"; + + if (!cp.beforeVersion(3,8) || security->getType() != secTypeNone) { + if (accept) { + os->writeU32(secResultOK); + } else { + os->writeU32(secResultFailed); + if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message + os->writeString(reason); + } + os->flush(); + } + + if (accept) { + state_ = RFBSTATE_INITIALISATION; + reader_ = new SMsgReaderV3(this, is); + writer_ = new SMsgWriterV3(&cp, os); + authSuccess(); + } else { + state_ = RFBSTATE_INVALID; + throw AuthFailureException(reason); + } +} + +void SConnection::setInitialColourMap() +{ +} + +void SConnection::clientInit(bool shared) +{ + writer_->writeServerInit(); + state_ = RFBSTATE_NORMAL; +} + +void SConnection::setPixelFormat(const PixelFormat& pf) +{ + SMsgHandler::setPixelFormat(pf); + readyForSetColourMapEntries = true; +} + +void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental) +{ + if (!readyForSetColourMapEntries) { + readyForSetColourMapEntries = true; + if (!cp.pf().trueColour) { + setInitialColourMap(); + } + } +} diff --git a/common/rfb/SConnection.h b/common/rfb/SConnection.h new file mode 100644 index 00000000..6b943f5e --- /dev/null +++ b/common/rfb/SConnection.h @@ -0,0 +1,193 @@ +/* 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. + */ +// +// 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/SSecurity.h> + +namespace rfb { + + class SMsgReader; + class SMsgWriter; + class SSecurity; + + class SConnection : public SMsgHandler { + public: + + SConnection(SSecurityFactory* sf, bool reverseConnection_); + 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); + + + // 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); + + // setInitialColourMap() is called when the client needs an initial + // SetColourMapEntries message. In fact this only happens when the client + // accepts the server's default pixel format and it uses a colour map. + virtual void setInitialColourMap(); + + // 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 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); } + + // deleteReaderAndWriter() deletes the reader and writer associated with + // this connection. This may be useful if you want to delete the streams + // before deleting the SConnection to make sure that no attempt by the + // SConnection is made to read or write. + // XXX Do we really need this at all??? + void deleteReaderAndWriter(); + + // 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* msg); + + // 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_; } + + // ssecurity() returns a pointer to this connection's SSecurity object, if + // any + const SSecurity* ssecurity() const { return security; } + + protected: + void setState(stateEnum s) { state_ = s; } + + bool readyForSetColourMapEntries; + + void processVersionMsg(); + void processSecurityTypeMsg(); + void processSecurityMsg(); + void processInitMsg(); + + int defaultMajorVersion, defaultMinorVersion; + rdr::InStream* is; + rdr::OutStream* os; + SMsgReader* reader_; + SMsgWriter* writer_; + SSecurity* security; + SSecurityFactory* securityFactory; + stateEnum state_; + bool reverseConnection; + }; +} +#endif diff --git a/common/rfb/SDesktop.h b/common/rfb/SDesktop.h new file mode 100644 index 00000000..7b054e37 --- /dev/null +++ b/common/rfb/SDesktop.h @@ -0,0 +1,121 @@ +/* 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> + +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* 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() {} + + // framebufferUpdateRequest() is called to let the desktop know that at + // least one client has become ready for an update. Desktops can check + // whether there are clients ready at any time by calling the VNCServer's + // clientsReadyForUpdate() method. + + virtual void framebufferUpdateRequest() {} + + // getFbSize() returns the current dimensions of the framebuffer. + // This can be called even while the SDesktop is not start()ed. + + virtual Point getFbSize() = 0; + + // 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; + buffer = new ManagedPixelBuffer(pf, size.x, size.y); + if (buffer) memset(buffer->data, 0, (pf.bpp/8) * (size.x*size.y)); + } + SStaticDesktop(const Point& size, const PixelFormat& pf) : buffer(0) { + buffer = new ManagedPixelBuffer(pf, size.x, size.y); + if (buffer) memset(buffer->data, 0, (pf.bpp/8) * (size.x*size.y)); + } + 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__ diff --git a/common/rfb/SFTMsgReader.cxx b/common/rfb/SFTMsgReader.cxx new file mode 100644 index 00000000..22787bf0 --- /dev/null +++ b/common/rfb/SFTMsgReader.cxx @@ -0,0 +1,201 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- SFTMsgReader.cxx + +#include <rfb/SFTMsgReader.h> + +using namespace rfb; + +SFTMsgReader::SFTMsgReader(rdr::InStream *pIS) +{ + m_pIS = pIS; +} + +SFTMsgReader::~SFTMsgReader() +{ +} + +bool +SFTMsgReader::readFileListRqst(unsigned int *pDirNameSize, char *pDirName, + unsigned int *pFlags) +{ + *pFlags = m_pIS->readU8(); + unsigned int dirNameSize = m_pIS->readU16(); + + if (dirNameSize >= FT_FILENAME_SIZE) { + m_pIS->skip(dirNameSize); + return false; + } else { + m_pIS->readBytes(pDirName, dirNameSize); + *pDirNameSize = dirNameSize; + pDirName[dirNameSize] = '\0'; + return true; + } +} + +bool +SFTMsgReader::readFileDownloadRqst(unsigned int *pFilenameSize, char *pFilename, + unsigned int *pPosition) +{ + unsigned char compressedLevel = 0; + return readU8U16U32StringMsg(&compressedLevel, pFilenameSize, pPosition, pFilename); +} + +bool +SFTMsgReader::readFileUploadRqst(unsigned int *pFilenameSize, char *pFilename, + unsigned int *pPosition) +{ + unsigned char compressedLevel = 0; + return readU8U16U32StringMsg(&compressedLevel, pFilenameSize, pPosition, pFilename); +} + +char * +SFTMsgReader::readFileUploadData(unsigned int *pDataSize, unsigned int *pModTime) +{ + unsigned char compressedLevel = m_pIS->readU8(); + unsigned int realSize = m_pIS->readU16(); + unsigned int compressedSize = m_pIS->readU16(); + + if ((realSize == 0) && (compressedSize == 0)) { + *pDataSize = 0; + *pModTime = m_pIS->readU32(); + return NULL; + } else { + char *pData = new char [compressedSize]; + m_pIS->readBytes(pData, compressedSize); + *pDataSize = compressedSize; + *pModTime = 0; + return pData; + } +} + +bool +SFTMsgReader::readFileCreateDirRqst(unsigned int *pDirNameSize, char *pDirName) +{ + return readU8U16StringMsg(pDirNameSize, pDirName); +} + +bool +SFTMsgReader::readFileDirSizeRqst(unsigned int *pDirNameSize, char *pDirName) +{ + return readU8U16StringMsg(pDirNameSize, pDirName); +} + +bool +SFTMsgReader::readFileDeleteRqst(unsigned int *pNameSize, char *pName) +{ + return readU8U16StringMsg(pNameSize, pName); +} + +bool +SFTMsgReader::readFileRenameRqst(unsigned int *pOldNameSize, + unsigned int *pNewNameSize, + char *pOldName, char *pNewName) +{ + m_pIS->skip(1); + + unsigned int oldNameSize = m_pIS->readU16(); + unsigned int newNameSize = m_pIS->readU16(); + + if ((oldNameSize >= *pOldNameSize) || (newNameSize >= *pNewNameSize)) { + m_pIS->skip(oldNameSize); + m_pIS->skip(newNameSize); + return false; + } + + if (oldNameSize != 0) { + m_pIS->readBytes(pOldName, oldNameSize); + pOldName[oldNameSize] = '\0'; + *pOldNameSize = oldNameSize; + } else { + *pOldNameSize = 0; + pOldName[0] = '\0'; + } + + if (newNameSize != 0) { + m_pIS->readBytes(pNewName, newNameSize); + pNewName[newNameSize] = '\0'; + } else { + *pNewNameSize = 0; + pNewName[0] = '\0'; + } + + return true; +} + +bool +SFTMsgReader::readFileDownloadCancel(unsigned int *pReasonSize, char *pReason) +{ + return readU8U16StringMsg(pReasonSize, pReason); +} + +bool +SFTMsgReader::readFileUploadFailed(unsigned int *pReasonSize, char *pReason) +{ + return readU8U16StringMsg(pReasonSize, pReason); +} + +bool +SFTMsgReader::readU8U16StringMsg(unsigned int *pReasonSize, char *pReason) +{ + m_pIS->skip(1); + unsigned int reasonSize = m_pIS->readU16(); + + if (reasonSize >= FT_FILENAME_SIZE) { + m_pIS->skip(reasonSize); + return false; + } else { + if (reasonSize == 0) { + pReason[0] = '\0'; + } else { + m_pIS->readBytes(pReason, reasonSize); + pReason[reasonSize] = '\0'; + } + *pReasonSize = reasonSize; + return true; + } +} + +bool +SFTMsgReader::readU8U16U32StringMsg(unsigned char *pU8, unsigned int *pU16, + unsigned int *pU32, char *pString) +{ + *pU8 = m_pIS->readU8(); + unsigned int strSize = m_pIS->readU16(); + *pU32 = m_pIS->readU32(); + + if (strSize >= FT_FILENAME_SIZE) { + m_pIS->skip(strSize); + return false; + } else { + *pU16 = strSize; + if (strSize == 0) { + pString[0] = '\0'; + } else { + m_pIS->readBytes(pString, strSize); + pString[strSize] = '\0'; + } + return true; + } +} diff --git a/common/rfb/SFTMsgReader.h b/common/rfb/SFTMsgReader.h new file mode 100644 index 00000000..32ac8697 --- /dev/null +++ b/common/rfb/SFTMsgReader.h @@ -0,0 +1,71 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- SFTMsgReader.h + +#ifndef __RFB_SFTMSGREADER_H__ +#define __RFB_SFTMSGREADER_H__ + +#include <rdr/InStream.h> +#include <rfb/fttypes.h> + +namespace rfb { + class SFTMsgReader + { + public: + SFTMsgReader(rdr::InStream *pIS); + ~SFTMsgReader(); + + bool readFileListRqst(unsigned int *pDirNameSize, char *pDirName, + unsigned int *pFlags); + + + bool readFileDownloadRqst(unsigned int *pFilenameSize, char *pFilename, + unsigned int *pPosition); + + bool readFileUploadRqst(unsigned int *pFilenameSize, char *pFilename, + unsigned int *pPosition); + + char *readFileUploadData(unsigned int *pDataSize, unsigned int *pModTime); + + + bool readFileCreateDirRqst(unsigned int *pDirNameSize, char *pDirName); + bool readFileDirSizeRqst(unsigned int *pDirNameSize, char *pDirName); + bool readFileDeleteRqst(unsigned int *pNameSize, char *pName); + + bool readFileRenameRqst(unsigned int *pOldNameSize, unsigned int *pNewNameSize, + char *pOldName, char *pNewName); + + bool readFileDownloadCancel(unsigned int *pReasonSize, char *pReason); + bool readFileUploadFailed(unsigned int *pReasonSize, char *pReason); + + private: + rdr::InStream *m_pIS; + + bool readU8U16StringMsg(unsigned int *pReasonSize, char *pReason); + bool readU8U16U32StringMsg(unsigned char *pU8, unsigned int *pU16, + unsigned int *pU32, char *pString); + }; +} + +#endif // __RFB_SFTMSGREADER_H__ diff --git a/common/rfb/SFTMsgWriter.cxx b/common/rfb/SFTMsgWriter.cxx new file mode 100644 index 00000000..fa6a82f9 --- /dev/null +++ b/common/rfb/SFTMsgWriter.cxx @@ -0,0 +1,152 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- SFTMsgWriter.cxx + +#include <rfb/SFTMsgWriter.h> + +using namespace rfb; + +SFTMsgWriter::SFTMsgWriter(rdr::OutStream *pOS) +{ + m_pOS = pOS; +} + +SFTMsgWriter::~SFTMsgWriter() +{ +} + +bool +SFTMsgWriter::writeFileListData(unsigned char flags, rfb::FileInfo *pFileInfo) +{ + unsigned int numFiles = pFileInfo->getNumEntries(); + + m_pOS->writeU8(msgTypeFileListData); + m_pOS->writeU8(flags); + m_pOS->writeU16(numFiles); + + if (numFiles == 0) { + m_pOS->writeU16(0); + m_pOS->writeU16(0); + } else { + unsigned int filenamesSize = pFileInfo->getFilenamesSize() + numFiles; + + m_pOS->writeU16(filenamesSize); + m_pOS->writeU16(filenamesSize); + + char *pFilenames = new char [filenamesSize]; + unsigned int pos = 0; + + for (unsigned int i = 0; i < numFiles; i++) { + char *pName = pFileInfo->getNameAt(i); + unsigned int len = strlen(pName); + + memcpy((void *)&pFilenames[pos], pName, len + 1); + pos += (len + 1); + + if (pFileInfo->getFlagsAt(i) & FT_ATTR_DIR) { + m_pOS->writeU32(FT_NET_ATTR_DIR); + } else { + m_pOS->writeU32(pFileInfo->getSizeAt(i)); + } + m_pOS->writeU32(pFileInfo->getDataAt(i)); + } + + m_pOS->writeBytes(pFilenames, filenamesSize); + + delete [] pFilenames; + } + + m_pOS->flush(); + + return true; +} + +bool +SFTMsgWriter::writeFileDownloadData(unsigned int dataSize, void *pData) +{ + m_pOS->writeU8(msgTypeFileDownloadData); + m_pOS->writeU8(0); + m_pOS->writeU16(dataSize); + m_pOS->writeU16(dataSize); + m_pOS->writeBytes(pData, dataSize); + m_pOS->flush(); + return true; +} + +bool +SFTMsgWriter::writeFileDownloadData(unsigned int modTime) +{ + m_pOS->writeU8(msgTypeFileDownloadData); + m_pOS->writeU8(0); + m_pOS->writeU16(0); + m_pOS->writeU16(0); + m_pOS->writeU32(modTime); + m_pOS->flush(); + return true; +} + +bool +SFTMsgWriter::writeFileUploadCancel(unsigned int reasonLen, char *pReason) +{ + m_pOS->writeU8(msgTypeFileUploadCancel); + return writeU8U16StringMsg(0, reasonLen, pReason); +} + +bool +SFTMsgWriter::writeFileDownloadFailed(unsigned int reasonLen, char *pReason) +{ + m_pOS->writeU8(msgTypeFileDownloadFailed); + return writeU8U16StringMsg(0, reasonLen, pReason); +} + +bool +SFTMsgWriter::writeFileDirSizeData(unsigned int dirSizeLow, + unsigned short dirSizeHigh) +{ + m_pOS->writeU8(msgTypeFileDirSizeData); + m_pOS->writeU8(0); + m_pOS->writeU16(dirSizeHigh); + m_pOS->writeU32(dirSizeLow); + m_pOS->flush(); + return true; +} + +bool +SFTMsgWriter::writeFileLastRqstFailed(unsigned char lastRequest, + unsigned short reasonLen, + char *pReason) +{ + m_pOS->writeU8(msgTypeFileLastRequestFailed); + return writeU8U16StringMsg(lastRequest, reasonLen, pReason); +} + +bool +SFTMsgWriter::writeU8U16StringMsg(unsigned char p1, unsigned short p2, char *pP3) +{ + m_pOS->writeU8(p1); + m_pOS->writeU16(p2); + m_pOS->writeBytes(pP3, p2); + m_pOS->flush(); + return true; +} diff --git a/common/rfb/SFTMsgWriter.h b/common/rfb/SFTMsgWriter.h new file mode 100644 index 00000000..f6bea9f3 --- /dev/null +++ b/common/rfb/SFTMsgWriter.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- SFTMsgWriter.h + +#ifndef __RFB_SFTMSGWRITER_H__ +#define __RFB_SFTMSGWRITER_H__ + +#include <rdr/OutStream.h> +#include <rfb/FileInfo.h> +#include <rfb/msgTypes.h> + +namespace rfb { + class SFTMsgWriter + { + public: + SFTMsgWriter(rdr::OutStream *pOS); + ~SFTMsgWriter(); + + bool writeFileListData(unsigned char flags, rfb::FileInfo *pFileInfo); + bool writeFileDownloadData(unsigned int dataSize, void *pData); + bool writeFileDownloadData(unsigned int modTime); + bool writeFileUploadCancel(unsigned int reasonLen, char *pReason); + bool writeFileDownloadFailed(unsigned int reasonLen, char *pReason); + bool writeFileDirSizeData(unsigned int dirSizeLow, unsigned short dirSizeHigh); + bool writeFileLastRqstFailed(unsigned char lastRequest, unsigned short reasonLen, + char *pReason); + + private: + rdr::OutStream *m_pOS; + + bool writeU8U16StringMsg(unsigned char p1, unsigned short p2, char *pP3); + }; +} + +#endif // __RFB_SFTMSGWRITER_H__ diff --git a/common/rfb/SFileTransfer.cxx b/common/rfb/SFileTransfer.cxx new file mode 100644 index 00000000..957e50b2 --- /dev/null +++ b/common/rfb/SFileTransfer.cxx @@ -0,0 +1,335 @@ +/* Copyright (C) 2006 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- SFileTransfer.cxx + +#include <rfb/msgTypes.h> +#include <rfb/SFileTransfer.h> + +using namespace rfb; + +SFileTransfer::SFileTransfer(network::Socket *sock) : + m_bUploadStarted(false), m_bDownloadStarted(false), + m_reader(&sock->inStream()), m_writer(&sock->outStream()), m_pSocket(sock) +{ +} + +SFileTransfer::~SFileTransfer() +{ +} + +bool +SFileTransfer::processMessages(int type) +{ + switch(type) + { + case msgTypeFileListRequest: + return processFileListRequest(); + case msgTypeFileDownloadRequest: + return processFileDownloadRequest(); + case msgTypeFileUploadRequest: + return processFileUploadRequest(); + case msgTypeFileUploadData: + return processFileUploadData(); + case msgTypeFileDownloadCancel: + return processFileDownloadCancel(); + case msgTypeFileUploadFailed: + return processFileUploadFailed(); + case msgTypeFileCreateDirRequest: + return processFileCreateDirRequest(); + case msgTypeFileDirSizeRequest: + return processFileDirSizeRequest(); + case msgTypeFileRenameRequest: + return processFileRenameRequest(); + case msgTypeFileDeleteRequest: + return processFileDeleteRequest(); + default: + return false; + } +} + +bool +SFileTransfer::processFileListRequest() +{ + char szDirName[FT_FILENAME_SIZE] = {0}; + unsigned int dirNameSize = FT_FILENAME_SIZE; + unsigned int flags = 0; + + if (!m_reader.readFileListRqst(&dirNameSize, szDirName, &flags)) return false; + + if (!convertPathFromNet(szDirName)) return false; + + bool bDirOnly = false; + if (flags & 0x10) bDirOnly = true; + + FileInfo fi; + if (!makeFileList(szDirName, &fi, bDirOnly)) { + flags = (flags | 0x80); + } + return m_writer.writeFileListData((unsigned char)flags, &fi); +} + +bool +SFileTransfer::processFileDownloadRequest() +{ + char szName[FT_FILENAME_SIZE] = {0}; + unsigned int nameSize = FT_FILENAME_SIZE; + unsigned int position = 0; + + if (!m_reader.readFileDownloadRqst(&nameSize, szName, &position)) return false; + + if (!convertPathFromNet(szName)) return false; + + if (m_bDownloadStarted) { + char reason[] = "The download is already started"; + m_writer.writeFileLastRqstFailed(msgTypeFileDownloadRequest, strlen(reason), reason); + return false; + } + + if (!m_fileReader.create(szName)) return false; + + m_bDownloadStarted = true; + + sendFileDownloadPortion(); + + return true; +} + +bool +SFileTransfer::sendFileDownloadPortion() +{ + char buffer[FT_MAX_SENDING_SIZE]; + unsigned int bytesRead = 0; + + if (m_fileReader.read((void *)buffer, FT_MAX_SENDING_SIZE, &bytesRead)) { + if (bytesRead == 0) { + m_writer.writeFileDownloadData(m_fileReader.getTime()); + m_fileReader.close(); + m_bDownloadStarted = false; + return true; + } else { + m_writer.writeFileDownloadData(bytesRead, buffer); + return initDownloadCallback(); + } + } else { + char reason[] = "Error while reading from file"; + m_writer.writeFileDownloadFailed(strlen(reason), reason); + m_fileReader.close(); + m_bDownloadStarted = false; + return true; + } +} + +bool +SFileTransfer::processFileUploadRequest() +{ + char szName[FT_FILENAME_SIZE] = {0}; + unsigned int nameSize = FT_FILENAME_SIZE; + unsigned int position = 0; + + if (!m_reader.readFileUploadRqst(&nameSize, szName, &position)) return false; + + if (!convertPathFromNet(szName)) return false; + + if (m_bUploadStarted) { + char reason[] = "The upload is already started"; + m_writer.writeFileLastRqstFailed(msgTypeFileUploadRequest, strlen(reason), reason); + return false; + } + + if (!m_fileWriter.create(szName)) { + char reason[] = "Can't create local file"; + m_writer.writeFileLastRqstFailed(msgTypeFileUploadRequest, strlen(reason), reason); + return true; + } + + m_bUploadStarted = true; + + return true; +} + +bool +SFileTransfer::processFileUploadData() +{ + unsigned int dataSize = 0; + unsigned int modTime = 0; + + char *pUploadData = m_reader.readFileUploadData(&dataSize, &modTime); + + if (!m_bUploadStarted) { + char reason[] = "Upload is impossible"; + m_writer.writeFileUploadCancel(strlen(reason), reason); + } else { + if (pUploadData == NULL) { + if (modTime == 0) { + char reason[] = "Upload failed"; + m_writer.writeFileUploadCancel(strlen(reason), reason); + } else { + m_fileWriter.setTime(modTime); + } + m_fileWriter.close(); + m_bUploadStarted = false; + } else { + unsigned int dataWritten = 0; + m_fileWriter.write(pUploadData, dataSize, &dataWritten); + if (dataWritten != dataSize) { + char reason[] = "Upload failed"; + m_writer.writeFileUploadCancel(strlen(reason), reason); + m_fileWriter.close(); + m_bUploadStarted = false; + } + } + } + delete [] pUploadData; + return true; +} + +bool +SFileTransfer::processFileDownloadCancel() +{ + char szReason[FT_FILENAME_SIZE] = {0}; + unsigned int reasonSize = FT_FILENAME_SIZE; + + if (!m_reader.readFileDownloadCancel(&reasonSize, szReason)) return false; + + m_fileReader.close(); + m_bDownloadStarted = false; + return true; +} + +bool +SFileTransfer::processFileUploadFailed() +{ + char szReason[FT_FILENAME_SIZE] = {0}; + unsigned int reasonSize = FT_FILENAME_SIZE; + + if (!m_reader.readFileUploadFailed(&reasonSize, szReason)) return false; + + deleteIt(m_fileWriter.getFilename()); + m_fileWriter.close(); + m_bUploadStarted = false; + return true; +} + +bool +SFileTransfer::processFileCreateDirRequest() +{ + char szName[FT_FILENAME_SIZE] = {0}; + unsigned int nameSize = FT_FILENAME_SIZE; + + if (!m_reader.readFileCreateDirRqst(&nameSize, szName)) return false; + + if (!convertPathFromNet(szName)) return false; + + return createDir(szName); +} + +bool +SFileTransfer::processFileDirSizeRequest() +{ + char szName[FT_FILENAME_SIZE] = {0}; + unsigned int nameSize = FT_FILENAME_SIZE; + + if (!m_reader.readFileDirSizeRqst(&nameSize, szName)) return false; + + if (!convertPathFromNet(szName)) return false; + + unsigned short highSize16 = 0; + unsigned int lowSize32 = 0; + + if (!getDirSize(szName, &highSize16, &lowSize32)) return false; + + return m_writer.writeFileDirSizeData(lowSize32, highSize16); +} + +bool +SFileTransfer::processFileRenameRequest() +{ + char szOldName[FT_FILENAME_SIZE] = {0}; + char szNewName[FT_FILENAME_SIZE] = {0}; + + unsigned int oldNameSize = FT_FILENAME_SIZE; + unsigned int newNameSize = FT_FILENAME_SIZE; + + if (!m_reader.readFileRenameRqst(&oldNameSize, &newNameSize, szOldName, szNewName)) return false; + + if ((!convertPathFromNet(szOldName)) || (!convertPathFromNet(szNewName))) return false; + + return renameIt(szOldName, szNewName); +} + +bool +SFileTransfer::processFileDeleteRequest() +{ + char szName[FT_FILENAME_SIZE] = {0}; + unsigned int nameSize = FT_FILENAME_SIZE; + + if (!m_reader.readFileDeleteRqst(&nameSize, szName)) return false; + + if (!convertPathFromNet(szName)) return false; + + return deleteIt(szName); +} + +bool +SFileTransfer::convertPathFromNet(char *pszPath) +{ + return true; +} + +bool +SFileTransfer::makeFileList(char *pszPath, FileInfo *pFI, bool bDirOnly) +{ + return false; +} + +bool +SFileTransfer::deleteIt(char *pszPath) +{ + return false; +} + +bool +SFileTransfer::renameIt(char *pszOldPath, char *pszNewPath) +{ + return false; +} + +bool +SFileTransfer::createDir(char *pszPath) +{ + return false; +} + +bool +SFileTransfer::getDirSize(char *pszName, unsigned short *pHighSize16, + unsigned int *pLowSize32) +{ + return false; +} + +bool +SFileTransfer::initDownloadCallback() +{ + return false; +} diff --git a/common/rfb/SFileTransfer.h b/common/rfb/SFileTransfer.h new file mode 100644 index 00000000..51a49282 --- /dev/null +++ b/common/rfb/SFileTransfer.h @@ -0,0 +1,83 @@ +/* Copyright (C) 2006 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- SFileTransfer.h + +#ifndef __RFB_SFILETRANSFER_H__ +#define __RFB_SFILETRANSFER_H__ + +#include <network/Socket.h> +#include <rfb/SFTMsgReader.h> +#include <rfb/SFTMsgWriter.h> +#include <rfb/FileWriter.h> +#include <rfb/FileReader.h> +#include <rfb/FileInfo.h> +#include <rfb/fttypes.h> + +namespace rfb { + class SFileTransfer + { + public: + SFileTransfer(network::Socket *sock); + virtual ~SFileTransfer(); + + bool processMessages(int type); + bool sendFileDownloadPortion(); + + protected: + bool processFileListRequest(); + bool processFileDownloadRequest(); + bool processFileUploadRequest(); + bool processFileUploadData(); + bool processFileDownloadCancel(); + bool processFileUploadFailed(); + bool processFileCreateDirRequest(); + bool processFileDirSizeRequest(); + bool processFileRenameRequest(); + bool processFileDeleteRequest(); + + virtual bool initDownloadCallback(); + virtual bool makeFileList(char *pszPath, FileInfo *pFI, bool bDirOnly); + virtual bool convertPathFromNet(char *pszPath); + + virtual bool deleteIt(char *pszPath); + virtual bool renameIt(char *pszOldPath, char *pszNewPath); + virtual bool createDir(char *pszPath); + + virtual bool getDirSize(char *pszName, unsigned short *pHighSize16, unsigned int *pLowSize32); + + bool m_bUploadStarted; + bool m_bDownloadStarted; + + private: + SFTMsgReader m_reader; + SFTMsgWriter m_writer; + + FileWriter m_fileWriter; + FileReader m_fileReader; + + network::Socket *m_pSocket; + }; +} + +#endif // __RFB_SFILETRANSFER_H__ diff --git a/common/rfb/SFileTransferManager.cxx b/common/rfb/SFileTransferManager.cxx new file mode 100644 index 00000000..999a079b --- /dev/null +++ b/common/rfb/SFileTransferManager.cxx @@ -0,0 +1,55 @@ +/* Copyright (C) 2006 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- SFileTransferManager.cxx + +#include <rfb/SFileTransferManager.h> + +using namespace rfb; + +SFileTransferManager::SFileTransferManager() +{ + +} + +SFileTransferManager::~SFileTransferManager() +{ + destroy(); +} + +void +SFileTransferManager::destroyObject(SFileTransfer *pFT) +{ + if (pFT == NULL) return; + + m_lstFTObjects.remove(pFT); + + delete pFT; +} + +void +SFileTransferManager::destroy() +{ + while(!m_lstFTObjects.empty()) + delete m_lstFTObjects.front(); +} diff --git a/common/rfb/SFileTransferManager.h b/common/rfb/SFileTransferManager.h new file mode 100644 index 00000000..fe816444 --- /dev/null +++ b/common/rfb/SFileTransferManager.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2006 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- SFileTransferManager.h + +#ifndef __RFB_SFILETRANSFERMANAGER_H__ +#define __RFB_SFILETRANSFERMANAGER_H__ + +#include <list> + +#include <rfb/SFileTransfer.h> +#include <network/Socket.h> + +namespace rfb { + class SFileTransferManager + { + public: + SFileTransferManager(); + virtual ~SFileTransferManager(); + + virtual SFileTransfer *createObject(network::Socket *sock) = 0; + void destroyObject(SFileTransfer *pFT); + + protected: + std::list<SFileTransfer*> m_lstFTObjects; + + void destroy(); + }; +} + +#endif // __RFB_SFILETRANSFERMANAGER_H__ diff --git a/common/rfb/SMsgHandler.cxx b/common/rfb/SMsgHandler.cxx new file mode 100644 index 00000000..ccc97ad0 --- /dev/null +++ b/common/rfb/SMsgHandler.cxx @@ -0,0 +1,52 @@ +/* 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/Exception.h> +#include <rfb/SMsgHandler.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, rdr::U32* encodings) +{ + cp.setEncodings(nEncodings, encodings); + supportsLocalCursor(); +} + +void SMsgHandler::framebufferUpdateRequest(const Rect& r, bool incremental) +{ +} + +void SMsgHandler::supportsLocalCursor() +{ +} diff --git a/common/rfb/SMsgHandler.h b/common/rfb/SMsgHandler.h new file mode 100644 index 00000000..cf3377d5 --- /dev/null +++ b/common/rfb/SMsgHandler.h @@ -0,0 +1,64 @@ +/* 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. + */ +// +// 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> + +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() and setEncodings() 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, rdr::U32* encodings); + virtual void framebufferUpdateRequest(const Rect& r, bool incremental); + + // 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(); + + virtual bool processFTMsg(int type) = 0; + + ConnParams cp; + }; +} +#endif diff --git a/common/rfb/SMsgReader.cxx b/common/rfb/SMsgReader.cxx new file mode 100644 index 00000000..f89e0f4f --- /dev/null +++ b/common/rfb/SMsgReader.cxx @@ -0,0 +1,97 @@ +/* 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 <rdr/InStream.h> +#include <rfb/Exception.h> +#include <rfb/util.h> +#include <rfb/SMsgHandler.h> +#include <rfb/SMsgReader.h> +#include <rfb/Configuration.h> + +using namespace rfb; + +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::readSetPixelFormat() +{ + is->skip(3); + PixelFormat pf; + pf.read(is); + handler->setPixelFormat(pf); +} + +void SMsgReader::readSetEncodings() +{ + is->skip(1); + int nEncodings = is->readU16(); + rdr::U32Array encodings(nEncodings); + for (int i = 0; i < nEncodings; i++) + encodings.buf[i] = is->readU32(); + handler->setEncodings(nEncodings, encodings.buf); +} + +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::readKeyEvent() +{ + bool down = is->readU8(); + is->skip(2); + rdr::U32 key = is->readU32(); + handler->keyEvent(key, 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 > maxCutText) { + 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->clientCutText(ca.buf, len); +} diff --git a/common/rfb/SMsgReader.h b/common/rfb/SMsgReader.h new file mode 100644 index 00000000..e6e40448 --- /dev/null +++ b/common/rfb/SMsgReader.h @@ -0,0 +1,56 @@ +/* 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. + */ +// +// 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: + virtual ~SMsgReader(); + + virtual void readClientInit()=0; + + // readMsg() reads a message, calling the handler as appropriate. + virtual void readMsg()=0; + + rdr::InStream* getInStream() { return is; } + + protected: + virtual void readSetPixelFormat(); + virtual void readSetEncodings(); + virtual void readFramebufferUpdateRequest(); + virtual void readKeyEvent(); + virtual void readPointerEvent(); + virtual void readClientCutText(); + + SMsgReader(SMsgHandler* handler, rdr::InStream* is); + + SMsgHandler* handler; + rdr::InStream* is; + }; +} +#endif diff --git a/common/rfb/SMsgReaderV3.cxx b/common/rfb/SMsgReaderV3.cxx new file mode 100644 index 00000000..be01b5db --- /dev/null +++ b/common/rfb/SMsgReaderV3.cxx @@ -0,0 +1,68 @@ +/* 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/PixelFormat.h> +#include <rfb/msgTypes.h> +#include <rfb/Exception.h> +#include <rdr/InStream.h> +#include <rfb/SMsgReaderV3.h> +#include <rfb/SMsgHandler.h> + +using namespace rfb; + +SMsgReaderV3::SMsgReaderV3(SMsgHandler* handler, rdr::InStream* is) + : SMsgReader(handler, is) +{ +} + +SMsgReaderV3::~SMsgReaderV3() +{ +} + +void SMsgReaderV3::readClientInit() +{ + bool shared = is->readU8(); + handler->clientInit(shared); +} + +void SMsgReaderV3::readMsg() +{ + int msgType = is->readU8(); + switch (msgType) { + case msgTypeSetPixelFormat: readSetPixelFormat(); break; + case msgTypeSetEncodings: readSetEncodings(); break; + case msgTypeFramebufferUpdateRequest: readFramebufferUpdateRequest(); break; + case msgTypeKeyEvent: readKeyEvent(); break; + case msgTypePointerEvent: readPointerEvent(); break; + case msgTypeClientCutText: readClientCutText(); break; + + case msgTypeFileListRequest: + case msgTypeFileDownloadRequest: + case msgTypeFileUploadRequest: + case msgTypeFileUploadData: + case msgTypeFileDownloadCancel: + case msgTypeFileUploadFailed: + case msgTypeFileCreateDirRequest: + case msgTypeFileDirSizeRequest: + case msgTypeFileRenameRequest: + case msgTypeFileDeleteRequest: handler->processFTMsg(msgType); break; + + default: + fprintf(stderr, "unknown message type %d\n", msgType); + throw Exception("unknown message type"); + } +} diff --git a/common/rfb/SMsgReaderV3.h b/common/rfb/SMsgReaderV3.h new file mode 100644 index 00000000..c6b7bf42 --- /dev/null +++ b/common/rfb/SMsgReaderV3.h @@ -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. + */ +#ifndef __RFB_SMSGREADERV3_H__ +#define __RFB_SMSGREADERV3_H__ + +#include <rfb/SMsgReader.h> + +namespace rfb { + class SMsgReaderV3 : public SMsgReader { + public: + SMsgReaderV3(SMsgHandler* handler, rdr::InStream* is); + virtual ~SMsgReaderV3(); + virtual void readClientInit(); + virtual void readMsg(); + }; +} +#endif diff --git a/common/rfb/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx new file mode 100644 index 00000000..085dfc16 --- /dev/null +++ b/common/rfb/SMsgWriter.cxx @@ -0,0 +1,202 @@ +/* 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 <assert.h> +#include <rdr/OutStream.h> +#include <rfb/msgTypes.h> +#include <rfb/ColourMap.h> +#include <rfb/ConnParams.h> +#include <rfb/UpdateTracker.h> +#include <rfb/SMsgWriter.h> +#include <rfb/LogWriter.h> + +using namespace rfb; + +static LogWriter vlog("SMsgWriter"); + +SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_) + : imageBufIdealSize(0), cp(cp_), os(os_), lenBeforeRect(0), + currentEncoding(0), updatesSent(0), rawBytesEquivalent(0), + imageBuf(0), imageBufSize(0) +{ + for (unsigned int i = 0; i <= encodingMax; i++) { + encoders[i] = 0; + bytesSent[i] = 0; + rectsSent[i] = 0; + } +} + +SMsgWriter::~SMsgWriter() +{ + vlog.info("framebuffer updates %d",updatesSent); + int bytes = 0; + for (unsigned int i = 0; i <= encodingMax; i++) { + delete encoders[i]; + if (i != encodingCopyRect) + bytes += bytesSent[i]; + if (rectsSent[i]) + vlog.info(" %s rects %d, bytes %d", + encodingName(i), rectsSent[i], bytesSent[i]); + } + vlog.info(" raw bytes equivalent %d, compression ratio %f", + rawBytesEquivalent, (double)rawBytesEquivalent / bytes); + delete [] imageBuf; +} + +void SMsgWriter::writeSetColourMapEntries(int firstColour, int nColours, + ColourMap* cm) +{ + startMsg(msgTypeSetColourMapEntries); + os->pad(1); + os->writeU16(firstColour); + os->writeU16(nColours); + for (int i = firstColour; i < firstColour+nColours; i++) { + int r, g, b; + cm->lookup(i, &r, &g, &b); + os->writeU16(r); + os->writeU16(g); + os->writeU16(b); + } + endMsg(); +} + +void SMsgWriter::writeBell() +{ + startMsg(msgTypeBell); + endMsg(); +} + +void SMsgWriter::writeServerCutText(const char* str, int len) +{ + startMsg(msgTypeServerCutText); + os->pad(3); + os->writeU32(len); + os->writeBytes(str, len); + endMsg(); +} + +void SMsgWriter::setupCurrentEncoder() +{ + unsigned int encoding = cp->currentEncoding(); + + // FIXME: Code duplication, see writeRect(). + if (!encoders[encoding]) { + encoders[encoding] = Encoder::createEncoder(encoding, this); + assert(encoders[encoding]); + } + + encoders[encoding]->setCompressLevel(cp->compressLevel); + encoders[encoding]->setQualityLevel(cp->qualityLevel); +} + +int SMsgWriter::getNumRects(const Rect &r) +{ + unsigned int encoding = cp->currentEncoding(); + + if (!encoders[encoding]) + setupCurrentEncoder(); + + return encoders[encoding]->getNumRects(r); +} + +// FIXME: This functions does not compute the number of rectangles correctly +// if the Tight encoder is used (but currently that does not matter +// because this function is never used). +void SMsgWriter::writeFramebufferUpdate(const UpdateInfo& ui, ImageGetter* ig, + Region* updatedRegion) +{ + writeFramebufferUpdateStart(ui.numRects()); + writeRects(ui, ig, updatedRegion); + writeFramebufferUpdateEnd(); +} + +void SMsgWriter::writeRects(const UpdateInfo& ui, ImageGetter* ig, + Region* updatedRegion) +{ + std::vector<Rect> rects; + std::vector<Rect>::const_iterator i; + updatedRegion->copyFrom(ui.changed); + updatedRegion->assign_union(ui.copied); + + ui.copied.get_rects(&rects, ui.copy_delta.x <= 0, ui.copy_delta.y <= 0); + for (i = rects.begin(); i != rects.end(); i++) + writeCopyRect(*i, i->tl.x - ui.copy_delta.x, i->tl.y - ui.copy_delta.y); + + ui.changed.get_rects(&rects); + for (i = rects.begin(); i != rects.end(); i++) { + Rect actual; + if (!writeRect(*i, ig, &actual)) { + updatedRegion->assign_subtract(*i); + updatedRegion->assign_union(actual); + } + } +} + + +bool SMsgWriter::needFakeUpdate() +{ + return false; +} + +bool SMsgWriter::writeRect(const Rect& r, ImageGetter* ig, Rect* actual) +{ + return writeRect(r, cp->currentEncoding(), ig, actual); +} + +bool SMsgWriter::writeRect(const Rect& r, unsigned int encoding, + ImageGetter* ig, Rect* actual) +{ + if (!encoders[encoding]) { + encoders[encoding] = Encoder::createEncoder(encoding, this); + assert(encoders[encoding]); + } + return encoders[encoding]->writeRect(r, ig, actual); +} + +void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY) +{ + startRect(r,encodingCopyRect); + os->writeU16(srcX); + os->writeU16(srcY); + endRect(); +} + +rdr::U8* SMsgWriter::getImageBuf(int required, int requested, int* nPixels) +{ + int requiredBytes = required * (cp->pf().bpp / 8); + int requestedBytes = requested * (cp->pf().bpp / 8); + int size = requestedBytes; + if (size > imageBufIdealSize) size = imageBufIdealSize; + + if (size < requiredBytes) + size = requiredBytes; + + if (imageBufSize < size) { + imageBufSize = size; + delete [] imageBuf; + imageBuf = new rdr::U8[imageBufSize]; + } + if (nPixels) + *nPixels = imageBufSize / (cp->pf().bpp / 8); + return imageBuf; +} + +int SMsgWriter::bpp() +{ + return cp->pf().bpp; +} diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h new file mode 100644 index 00000000..ed8ad0ef --- /dev/null +++ b/common/rfb/SMsgWriter.h @@ -0,0 +1,163 @@ +/* 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. + */ +// +// SMsgWriter - class for writing RFB messages on the server side. +// + +#ifndef __RFB_SMSGWRITER_H__ +#define __RFB_SMSGWRITER_H__ + +#include <rdr/types.h> +#include <rfb/encodings.h> +#include <rfb/Encoder.h> + +namespace rdr { class OutStream; } + +namespace rfb { + + class PixelFormat; + class ConnParams; + class ImageGetter; + class ColourMap; + class Region; + class UpdateInfo; + + class WriteSetCursorCallback { + public: + virtual void writeSetCursorCallback() = 0; + }; + + class SMsgWriter { + public: + virtual ~SMsgWriter(); + + // writeServerInit() must only be called at the appropriate time in the + // protocol initialisation. + virtual void writeServerInit()=0; + + // Methods to write normal protocol messages + + // writeSetColourMapEntries() writes a setColourMapEntries message, using + // the given ColourMap object to lookup the RGB values of the given range + // of colours. + virtual void writeSetColourMapEntries(int firstColour, int nColours, + ColourMap* cm); + + // writeBell() and writeServerCutText() do the obvious thing. + virtual void writeBell(); + virtual void writeServerCutText(const char* str, int len); + + // setupCurrentEncoder() should be called before each framebuffer update, + // prior to calling getNumRects() or writeFramebufferUpdateStart(). + void setupCurrentEncoder(); + + // getNumRects() computes the number of sub-rectangles that will compose a + // given rectangle, for current encoder. + int getNumRects(const Rect &r); + + // writeSetDesktopSize() on a V3 writer won't actually write immediately, + // but will write the relevant pseudo-rectangle as part of the next update. + virtual bool writeSetDesktopSize()=0; + + // Like setDesktopSize, we can't just write out a setCursor message + // immediately on a V3 writer. Instead of calling writeSetCursor() + // directly, you must call cursorChange(), and then invoke writeSetCursor() + // in response to the writeSetCursorCallback() callback. For a V3 writer + // this will happen when the next update is sent. + virtual void cursorChange(WriteSetCursorCallback* cb)=0; + virtual void writeSetCursor(int width, int height, const Point& hotspot, + void* data, void* mask)=0; + virtual void writeSetXCursor(int width, int height, int hotspotX, + int hotspotY, void* data, void* mask)=0; + + // needFakeUpdate() returns true when an immediate update is needed in + // order to flush out setDesktopSize or setCursor pseudo-rectangles to the + // client. + virtual bool needFakeUpdate(); + + // writeFramebufferUpdate() writes a framebuffer update using the given + // UpdateInfo and ImageGetter. On a V3 writer this may have + // pseudo-rectangles for setDesktopSize and setCursor added to it, and so + // may invoke writeSetCursorCallback(). + virtual void writeFramebufferUpdate(const UpdateInfo& ui, ImageGetter* ig, + Region* updatedRegion); + + // writeRects() accepts an UpdateInfo (changed & copied regions) and an + // ImageGetter to fetch pixels from. It then calls writeCopyRect() and + // writeRect() as appropriate. writeFramebufferUpdateStart() must be used + // before the first writeRects() call and writeFrameBufferUpdateEnd() after + // the last one. It returns the actual region sent to the client, which + // may be smaller than the update passed in. + virtual void writeRects(const UpdateInfo& update, ImageGetter* ig, + Region* updatedRegion); + + // To construct a framebuffer update you can call + // writeFramebufferUpdateStart(), followed by a number of writeCopyRect()s + // and writeRect()s, finishing with writeFramebufferUpdateEnd(). If you + // know the exact number of rectangles ahead of time you can specify it to + // writeFramebufferUpdateStart() which can be more efficient. + virtual void writeFramebufferUpdateStart(int nRects)=0; + virtual void writeFramebufferUpdateStart()=0; + virtual void writeFramebufferUpdateEnd()=0; + + // writeRect() tries to write the given rectangle. If it is unable to + // write the whole rectangle it returns false and sets actual to the actual + // rectangle which was updated. + virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual); + virtual bool writeRect(const Rect& r, unsigned int encoding, + ImageGetter* ig, Rect* actual); + + virtual void writeCopyRect(const Rect& r, int srcX, int srcY); + + virtual void startRect(const Rect& r, unsigned int enc)=0; + virtual void endRect()=0; + + ConnParams* getConnParams() { return cp; } + rdr::OutStream* getOutStream() { return os; } + rdr::U8* getImageBuf(int required, int requested=0, int* nPixels=0); + int bpp(); + + int getUpdatesSent() { return updatesSent; } + int getRectsSent(int encoding) { return rectsSent[encoding]; } + int getBytesSent(int encoding) { return bytesSent[encoding]; } + int getRawBytesEquivalent() { return rawBytesEquivalent; } + + int imageBufIdealSize; + + protected: + SMsgWriter(ConnParams* cp, rdr::OutStream* os); + + virtual void startMsg(int type)=0; + virtual void endMsg()=0; + + ConnParams* cp; + rdr::OutStream* os; + + Encoder* encoders[encodingMax+1]; + int lenBeforeRect; + unsigned int currentEncoding; + int updatesSent; + int bytesSent[encodingMax+1]; + int rectsSent[encodingMax+1]; + int rawBytesEquivalent; + + rdr::U8* imageBuf; + int imageBufSize; + }; +} +#endif diff --git a/common/rfb/SMsgWriterV3.cxx b/common/rfb/SMsgWriterV3.cxx new file mode 100644 index 00000000..a85f85ea --- /dev/null +++ b/common/rfb/SMsgWriterV3.cxx @@ -0,0 +1,197 @@ +/* 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/OutStream.h> +#include <rdr/MemOutStream.h> +#include <rfb/msgTypes.h> +#include <rfb/Exception.h> +#include <rfb/ConnParams.h> +#include <rfb/SMsgWriterV3.h> + +using namespace rfb; + +SMsgWriterV3::SMsgWriterV3(ConnParams* cp, rdr::OutStream* os) + : SMsgWriter(cp, os), updateOS(0), realOS(os), nRectsInUpdate(0), + nRectsInHeader(0), wsccb(0), + needSetDesktopSize(false) +{ +} + +SMsgWriterV3::~SMsgWriterV3() +{ + delete updateOS; +} + +void SMsgWriterV3::writeServerInit() +{ + os->writeU16(cp->width); + os->writeU16(cp->height); + cp->pf().write(os); + os->writeString(cp->name()); + endMsg(); +} + +void SMsgWriterV3::startMsg(int type) +{ + if (os != realOS) + throw Exception("startMsg called while writing an update?"); + + os->writeU8(type); +} + +void SMsgWriterV3::endMsg() +{ + os->flush(); +} + +bool SMsgWriterV3::writeSetDesktopSize() { + if (!cp->supportsDesktopResize) return false; + needSetDesktopSize = true; + return true; +} + +void SMsgWriterV3::cursorChange(WriteSetCursorCallback* cb) +{ + wsccb = cb; +} + +void SMsgWriterV3::writeSetCursor(int width, int height, const Point& hotspot, + void* data, void* mask) +{ + if (!wsccb) return; + if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) + throw Exception("SMsgWriterV3::writeSetCursor: nRects out of sync"); + os->writeS16(hotspot.x); + os->writeS16(hotspot.y); + os->writeU16(width); + os->writeU16(height); + os->writeU32(pseudoEncodingCursor); + os->writeBytes(data, width * height * (cp->pf().bpp/8)); + os->writeBytes(mask, (width+7)/8 * height); +} + +void SMsgWriterV3::writeSetXCursor(int width, int height, int hotspotX, + int hotspotY, void* data, void* mask) +{ + if (!wsccb) return; + if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) + throw Exception("SMsgWriterV3::writeSetXCursor: nRects out of sync"); + os->writeS16(hotspotX); + os->writeS16(hotspotY); + os->writeU16(width); + os->writeU16(height); + os->writeU32(pseudoEncodingXCursor); + // FIXME: We only support black and white cursors, currently. We + // could pass the correct color by using the pix0/pix1 values + // returned from getBitmap, in writeSetCursorCallback. However, we + // would then need to undo the conversion from rgb to Pixel that is + // done by FakeAllocColor. + if (width * height) { + os->writeU8(0); + os->writeU8(0); + os->writeU8(0); + os->writeU8(255); + os->writeU8(255); + os->writeU8(255); + os->writeBytes(data, (width+7)/8 * height); + os->writeBytes(mask, (width+7)/8 * height); + } +} + +void SMsgWriterV3::writeFramebufferUpdateStart(int nRects) +{ + startMsg(msgTypeFramebufferUpdate); + os->pad(1); + if (wsccb) nRects++; + if (needSetDesktopSize) nRects++; + os->writeU16(nRects); + nRectsInUpdate = 0; + nRectsInHeader = nRects; + if (wsccb) { + wsccb->writeSetCursorCallback(); + wsccb = 0; + } +} + +void SMsgWriterV3::writeFramebufferUpdateStart() +{ + nRectsInUpdate = nRectsInHeader = 0; + if (!updateOS) + updateOS = new rdr::MemOutStream; + os = updateOS; +} + +void SMsgWriterV3::writeFramebufferUpdateEnd() +{ + if (needSetDesktopSize) { + if (!cp->supportsDesktopResize) + throw Exception("Client does not support desktop resize"); + if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) + throw Exception("SMsgWriterV3 setDesktopSize: nRects out of sync"); + os->writeS16(0); + os->writeS16(0); + os->writeU16(cp->width); + os->writeU16(cp->height); + os->writeU32(pseudoEncodingDesktopSize); + needSetDesktopSize = false; + } + + if (nRectsInUpdate != nRectsInHeader && nRectsInHeader) + throw Exception("SMsgWriterV3::writeFramebufferUpdateEnd: " + "nRects out of sync"); + if (os == updateOS) { + os = realOS; + startMsg(msgTypeFramebufferUpdate); + os->pad(1); + os->writeU16(nRectsInUpdate); + os->writeBytes(updateOS->data(), updateOS->length()); + updateOS->clear(); + } + + updatesSent++; + endMsg(); +} + +bool SMsgWriterV3::needFakeUpdate() +{ + return wsccb || needSetDesktopSize; +} + +void SMsgWriterV3::startRect(const Rect& r, unsigned int encoding) +{ + if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) + throw Exception("SMsgWriterV3::startRect: nRects out of sync"); + + currentEncoding = encoding; + lenBeforeRect = os->length(); + if (encoding != encodingCopyRect) + rawBytesEquivalent += 12 + r.width() * r.height() * (bpp()/8); + + os->writeS16(r.tl.x); + os->writeS16(r.tl.y); + os->writeU16(r.width()); + os->writeU16(r.height()); + os->writeU32(encoding); +} + +void SMsgWriterV3::endRect() +{ + if (currentEncoding <= encodingMax) { + bytesSent[currentEncoding] += os->length() - lenBeforeRect; + rectsSent[currentEncoding]++; + } +} diff --git a/common/rfb/SMsgWriterV3.h b/common/rfb/SMsgWriterV3.h new file mode 100644 index 00000000..501fa489 --- /dev/null +++ b/common/rfb/SMsgWriterV3.h @@ -0,0 +1,57 @@ +/* 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_SMSGWRITERV3_H__ +#define __RFB_SMSGWRITERV3_H__ + +#include <rfb/SMsgWriter.h> + +namespace rdr { class MemOutStream; } + +namespace rfb { + class SMsgWriterV3 : public SMsgWriter { + public: + SMsgWriterV3(ConnParams* cp, rdr::OutStream* os); + virtual ~SMsgWriterV3(); + + virtual void writeServerInit(); + virtual void startMsg(int type); + virtual void endMsg(); + virtual bool writeSetDesktopSize(); + virtual void cursorChange(WriteSetCursorCallback* cb); + virtual void writeSetCursor(int width, int height, const Point& hotspot, + void* data, void* mask); + virtual void writeSetXCursor(int width, int height, int hotspotX, + int hotspotY, void* data, void* mask); + virtual void writeFramebufferUpdateStart(int nRects); + virtual void writeFramebufferUpdateStart(); + virtual void writeFramebufferUpdateEnd(); + virtual bool needFakeUpdate(); + virtual void startRect(const Rect& r, unsigned int encoding); + virtual void endRect(); + + private: + rdr::MemOutStream* updateOS; + rdr::OutStream* realOS; + int nRectsInUpdate; + int nRectsInHeader; + WriteSetCursorCallback* wsccb; + bool needSetDesktopSize; + bool needLastRect; + }; +} +#endif diff --git a/common/rfb/SSecurity.h b/common/rfb/SSecurity.h new file mode 100644 index 00000000..108985b3 --- /dev/null +++ b/common/rfb/SSecurity.h @@ -0,0 +1,84 @@ +/* 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. + */ +// +// SSecurity - class on the server 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 client. It +// should return false when it needs more data, or true when the connection has +// been successfully authenticated. In the event of authentication failure an +// AuthFailureException should be thrown - this will result in a "failed" +// security result being sent to the client with the str() from the exception +// being sent as the reason. Any other type of failure should be indicated by +// some other kind of exception which will cause the connection to be aborted. +// +// processMsg() must never block (or at least must never block until the client +// has been authenticated) - this is to prevent denial of service attacks. +// Note that the first time processMsg() is called, there is no guarantee that +// there is any data to read from the SConnection's InStream, but subsequent +// calls guarantee there is at least one byte which can be read without +// blocking. +// +// getType() should return the secType value corresponding to the SSecurity +// implementation. +// + +#ifndef __RFB_SSECURITY_H__ +#define __RFB_SSECURITY_H__ + +#include <rdr/types.h> +#include <rfb/util.h> +#include <list> + +namespace rfb { + + class SConnection; + + class SSecurity { + public: + virtual ~SSecurity() {} + virtual bool processMsg(SConnection* sc)=0; + virtual void destroy() { delete this; } + virtual int getType() const = 0; + + // getUserName() gets the name of the user attempting authentication. The + // storage is owned by the SSecurity object, so a copy must be taken if + // necessary. Null may be returned to indicate that there is no user name + // for this security type. + virtual const char* getUserName() const = 0; + }; + + // SSecurityFactory creates new SSecurity instances for + // particular security types. + // The instances must be destroyed by calling destroy() + // on them when done. + // getSecTypes returns a list of the security types that are both configured + // and actually supported. Which configuration is considered depends on the + // reverseConnection parameter. + class SSecurityFactory { + public: + virtual ~SSecurityFactory() {} + virtual SSecurity* getSSecurity(rdr::U8 secType, bool noAuth=false)=0; + virtual void getSecTypes(std::list<rdr::U8>* secTypes, + bool reverseConnection) = 0; + }; + +} +#endif diff --git a/common/rfb/SSecurityFactoryStandard.cxx b/common/rfb/SSecurityFactoryStandard.cxx new file mode 100644 index 00000000..a0726986 --- /dev/null +++ b/common/rfb/SSecurityFactoryStandard.cxx @@ -0,0 +1,128 @@ +/* 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. + */ +// +// SSecurityFactoryStandard +// + +#include <rfb/secTypes.h> +#include <rfb/SSecurityNone.h> +#include <rfb/Configuration.h> +#include <rfb/LogWriter.h> +#include <rfb/Exception.h> +#include <rfb/SSecurityFactoryStandard.h> +#include <rfb/Password.h> + +using namespace rfb; + +static LogWriter vlog("SSecurityFactoryStandard"); + +StringParameter SSecurityFactoryStandard::sec_types +("SecurityTypes", + "Specify which security scheme to use for incoming connections (None, VncAuth)", + "VncAuth"); + +StringParameter SSecurityFactoryStandard::rev_sec_types +("ReverseSecurityTypes", + "Specify encryption scheme to use for reverse connections (None)", + "None"); + + +StringParameter SSecurityFactoryStandard::vncAuthPasswdFile +("PasswordFile", "Password file for VNC authentication", ""); +VncAuthPasswdParameter SSecurityFactoryStandard::vncAuthPasswd +("Password", "Obfuscated binary encoding of the password which clients must supply to " + "access the server", &SSecurityFactoryStandard::vncAuthPasswdFile); + + +SSecurity* SSecurityFactoryStandard::getSSecurity(rdr::U8 secType, bool reverseConnection) { + switch (secType) { + case secTypeNone: return new SSecurityNone(); + case secTypeVncAuth: + return new SSecurityVncAuth(&vncAuthPasswd); + default: + throw Exception("Security type not supported"); + } +} + +void SSecurityFactoryStandard::getSecTypes(std::list<rdr::U8>* secTypes, bool reverseConnection) { + CharArray secTypesStr; + if (reverseConnection) + secTypesStr.buf = rev_sec_types.getData(); + else + secTypesStr.buf = sec_types.getData(); + std::list<int> configured = parseSecTypes(secTypesStr.buf); + std::list<int>::iterator i; + for (i=configured.begin(); i!=configured.end(); i++) { + if (isSecTypeSupported(*i)) + secTypes->push_back(*i); + } +} + +bool SSecurityFactoryStandard::isSecTypeSupported(rdr::U8 secType) { + switch (secType) { + case secTypeNone: + case secTypeVncAuth: + return true; + default: + return false; + } +} + + +VncAuthPasswdParameter::VncAuthPasswdParameter(const char* name, + const char* desc, + StringParameter* passwdFile_) +: BinaryParameter(name, desc, 0, 0), passwdFile(passwdFile_) { +} + +char* VncAuthPasswdParameter::getVncAuthPasswd() { + ObfuscatedPasswd obfuscated; + getData((void**)&obfuscated.buf, &obfuscated.length); + + if (obfuscated.length == 0) { + if (passwdFile) { + CharArray fname(passwdFile->getData()); + if (!fname.buf[0]) { + vlog.info("neither %s nor %s params set", getName(), passwdFile->getName()); + return 0; + } + + FILE* fp = fopen(fname.buf, "r"); + if (!fp) { + vlog.error("opening password file '%s' failed",fname.buf); + return 0; + } + + vlog.debug("reading password file"); + obfuscated.buf = new char[128]; + obfuscated.length = fread(obfuscated.buf, 1, 128, fp); + fclose(fp); + } else { + vlog.info("%s parameter not set", getName()); + } + } + + try { + PlainPasswd password(obfuscated); + return password.takeBuf(); + } catch (...) { + return 0; + } +} + + diff --git a/common/rfb/SSecurityFactoryStandard.h b/common/rfb/SSecurityFactoryStandard.h new file mode 100644 index 00000000..165881ec --- /dev/null +++ b/common/rfb/SSecurityFactoryStandard.h @@ -0,0 +1,68 @@ +/* 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. + */ + +// +// SSecurityFactoryStandard - implementation of the SSecurityFactory +// interface. +// +// Server implementations must define an instance of a +// VncAuthPasswdParameter-based class somewhere. Any class based on +// VncAuthPasswdParameter will automatically register itself as the +// password parameter to be used by the Standard factory. +// +// Two implementations are provided here: +// +// VncAuthPasswdConfigParameter - reads the password from the Binary +// parameter "Password". +// VncAuthPasswdFileParameter - reads the password from the file named +// in the String parameter "PasswordFile". +// +// This factory supports only the "None" and "VncAuth" security types. +// + +#ifndef __RFB_SSECURITYFACTORY_STANDARD_H__ +#define __RFB_SSECURITYFACTORY_STANDARD_H__ + +#include <rfb/SSecurityVncAuth.h> +#include <rfb/Configuration.h> +#include <rfb/util.h> + +namespace rfb { + + class VncAuthPasswdParameter : public VncAuthPasswdGetter, BinaryParameter { + public: + VncAuthPasswdParameter(const char* name, const char* desc, StringParameter* passwdFile_); + virtual char* getVncAuthPasswd(); + protected: + StringParameter* passwdFile; + }; + + class SSecurityFactoryStandard : public SSecurityFactory { + public: + virtual SSecurity* getSSecurity(rdr::U8 secType, bool reverse); + virtual void getSecTypes(std::list<rdr::U8>* secTypes, bool reverse); + static StringParameter sec_types; + static StringParameter rev_sec_types; + static StringParameter vncAuthPasswdFile; + static VncAuthPasswdParameter vncAuthPasswd; + protected: + virtual bool isSecTypeSupported(rdr::U8 secType); + }; + +} +#endif diff --git a/common/rfb/SSecurityNone.h b/common/rfb/SSecurityNone.h new file mode 100644 index 00000000..5c19f290 --- /dev/null +++ b/common/rfb/SSecurityNone.h @@ -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. + */ +// +// SSecurityNone.h +// + +#ifndef __SSECURITYNONE_H__ +#define __SSECURITYNONE_H__ + +#include <rfb/SSecurity.h> + +namespace rfb { + + class SSecurityNone : public SSecurity { + public: + virtual bool processMsg(SConnection* sc) { return true; } + virtual int getType() const {return secTypeNone;} + virtual const char* getUserName() const {return 0;} + }; +} +#endif diff --git a/common/rfb/SSecurityVncAuth.cxx b/common/rfb/SSecurityVncAuth.cxx new file mode 100644 index 00000000..29a3b964 --- /dev/null +++ b/common/rfb/SSecurityVncAuth.cxx @@ -0,0 +1,87 @@ +/* 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. + */ +// +// SSecurityVncAuth +// +// XXX not thread-safe, because d3des isn't - do we need to worry about this? +// + +#include <rfb/SSecurityVncAuth.h> +#include <rdr/RandomStream.h> +#include <rfb/SConnection.h> +#include <rfb/Password.h> +#include <rfb/Configuration.h> +#include <rfb/LogWriter.h> +#include <rfb/util.h> +#include <rfb/Exception.h> +#include <string.h> +#include <stdio.h> +extern "C" { +#include <rfb/d3des.h> +} + + +using namespace rfb; + +static LogWriter vlog("SVncAuth"); + + +SSecurityVncAuth::SSecurityVncAuth(VncAuthPasswdGetter* pg_) + : sentChallenge(false), responsePos(0), pg(pg_) +{ +} + +bool SSecurityVncAuth::processMsg(SConnection* sc) +{ + rdr::InStream* is = sc->getInStream(); + rdr::OutStream* os = sc->getOutStream(); + + if (!sentChallenge) { + rdr::RandomStream rs; + rs.readBytes(challenge, vncAuthChallengeSize); + os->writeBytes(challenge, vncAuthChallengeSize); + os->flush(); + sentChallenge = true; + return false; + } + + while (responsePos < vncAuthChallengeSize && is->checkNoWait(1)) + response[responsePos++] = is->readU8(); + + if (responsePos < vncAuthChallengeSize) return false; + + PlainPasswd passwd(pg->getVncAuthPasswd()); + + if (!passwd.buf) + throw AuthFailureException("No password configured for VNC Auth"); + + // Calculate the expected 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); + + // Check the actual response + if (memcmp(challenge, response, vncAuthChallengeSize) != 0) + throw AuthFailureException(); + + return true; +} diff --git a/common/rfb/SSecurityVncAuth.h b/common/rfb/SSecurityVncAuth.h new file mode 100644 index 00000000..1d0a82de --- /dev/null +++ b/common/rfb/SSecurityVncAuth.h @@ -0,0 +1,55 @@ +/* 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. + */ +// SSecurityVncAuth - legacy VNC authentication protocol. +// The getPasswd call can be overridden if you wish to store +// the VncAuth password in an implementation-specific place. +// Otherwise, the password is read from a BinaryParameter +// called Password. + +#ifndef __RFB_SSECURITYVNCAUTH_H__ +#define __RFB_SSECURITYVNCAUTH_H__ + +#include <rfb/SSecurity.h> +#include <rfb/secTypes.h> +#include <rdr/types.h> + +namespace rfb { + + class VncAuthPasswdGetter { + public: + // getPasswd() returns a string or null if unsuccessful. The + // SSecurityVncAuth object delete[]s the string when done. + virtual char* getVncAuthPasswd()=0; + }; + + class SSecurityVncAuth : public SSecurity { + public: + SSecurityVncAuth(VncAuthPasswdGetter* pg); + virtual bool processMsg(SConnection* sc); + virtual int getType() const {return secTypeVncAuth;} + virtual const char* getUserName() const {return 0;} + private: + enum {vncAuthChallengeSize = 16}; + rdr::U8 challenge[vncAuthChallengeSize]; + rdr::U8 response[vncAuthChallengeSize]; + bool sentChallenge; + int responsePos; + VncAuthPasswdGetter* pg; + }; +} +#endif diff --git a/common/rfb/ScaledPixelBuffer.cxx b/common/rfb/ScaledPixelBuffer.cxx new file mode 100644 index 00000000..bf4612de --- /dev/null +++ b/common/rfb/ScaledPixelBuffer.cxx @@ -0,0 +1,132 @@ +/* Copyright (C) 2005 TightVNC Team. 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. + */ + +// -=- ScaledPixelBuffer.cxx + +#include <rfb/ScaledPixelBuffer.h> + +#include <math.h> +#include <memory.h> + +using namespace rdr; +using namespace rfb; + +ScaledPixelBuffer::ScaledPixelBuffer(U8 **src_data_, int src_width_, + int src_height_, int scale) + : bpp(32), scaled_data(0), scale_ratio(1), scale(100) { + + setSourceBuffer(src_data_, src_width_, src_height_); +} + +ScaledPixelBuffer::ScaledPixelBuffer() + : src_data(0), src_width(0), src_height(0), scale_ratio(1), scale(100), + bpp(32), scaled_data(0) { +} + +ScaledPixelBuffer::~ScaledPixelBuffer() { +} + +void ScaledPixelBuffer::setSourceBuffer(U8 **src_data_, int w, int h) { + src_data = src_data_; + src_width = w; + src_height = h; + calculateScaledBufferSize(); + recreateScaledBuffer(); +} + +void ScaledPixelBuffer::setScale(int scale_) { + if (scale != scale_) { + scale = scale_; + scale_ratio = double(scale) / 100; + calculateScaledBufferSize(); + recreateScaledBuffer(); + } +} + +void ScaledPixelBuffer::scaleRect(const Rect& r) { + static U8 *src_ptr, *ptr; + static U8 r0, r1, r2, r3; + static U8 g0, g1, g2, g3; + static U8 b0, b1, b2, b3; + static double c1_sub_dx, c1_sub_dy; + static double dx, dy; + static int i, j; + static Rect changed_rect; + + // Calculate the changed pixel rect in the scaled image + changed_rect = calculateScaleBoundary(r); + + // Scale the source rect to the destination image buffer using + // bilinear interplation + for (int y = changed_rect.tl.y; y < changed_rect.br.y; y++) { + j = (int)(dy = y / scale_ratio); + dy -= j; + c1_sub_dy = 1 - dy; + + for (int x = changed_rect.tl.x; x < changed_rect.br.x; x++) { + ptr = &scaled_data[(x + y*scaled_width) * 4]; + + i = (int)(dx = x / scale_ratio); + dx -= i; + c1_sub_dx = 1 - dx; + + src_ptr = &(*src_data)[(i + (j*src_width))*4]; + b0 = *src_ptr; g0 = *(src_ptr+1); r0 = *(src_ptr+2); + if (i+1 < src_width) { + b1 = *(src_ptr+4); g1 = *(src_ptr+5); r1 = *(src_ptr+6); + } else { + b1 = b0; r1 = r0; g1 = g0; + } + if (j+1 < src_height) { + src_ptr += src_width * 4; + b3 = *src_ptr; g3 = *(src_ptr+1); r3 = *(src_ptr+2); + } else { + b3 = b0; r3 = r0; g3 = g0; + } + if ((i+1 < src_width) && (j+1 < src_height)) { + b2 = *(src_ptr+4); g2 = *(src_ptr+5); r2 = *(src_ptr+6); + } else if (i+1 >= src_width) { + b2 = b3; r2 = r3; g2 = g3; + } else { + b2 = b1; r2 = r1; g2 = g1; + } + *ptr++ = (U8)((b0*c1_sub_dx+b1*dx)*c1_sub_dy + (b3*c1_sub_dx+b2*dx)*dy); + *ptr++ = (U8)((g0*c1_sub_dx+g1*dx)*c1_sub_dy + (g3*c1_sub_dx+g2*dx)*dy); + *ptr = (U8)((r0*c1_sub_dx+r1*dx)*c1_sub_dy + (r3*c1_sub_dx+r2*dx)*dy); + } + } +} + +Rect ScaledPixelBuffer::calculateScaleBoundary(const Rect& r) { + static int x_start, y_start, x_end, y_end; + x_start = r.tl.x == 0 ? 0 : ceil((r.tl.x-1) * scale_ratio); + y_start = r.tl.y == 0 ? 0 : ceil((r.tl.y-1) * scale_ratio); + x_end = ceil(r.br.x * scale_ratio - 1); + x_end = x_end < scaled_width ? x_end + 1 : scaled_width; + y_end = ceil(r.br.y * scale_ratio - 1); + y_end = y_end < scaled_height ? y_end + 1 : scaled_height; + return Rect(x_start, y_start, x_end, y_end); +} + +void ScaledPixelBuffer::calculateScaledBufferSize() { + scaled_width = (int)ceil(src_width * scale_ratio); + scaled_height = (int)ceil(src_height * scale_ratio); +} + +void ScaledPixelBuffer::recreateScaledBuffer() { +} diff --git a/common/rfb/ScaledPixelBuffer.h b/common/rfb/ScaledPixelBuffer.h new file mode 100644 index 00000000..3b6aa7ef --- /dev/null +++ b/common/rfb/ScaledPixelBuffer.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2005 TightVNC Team. 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. + */ + +// -=- ScaledPixelBuffer.h +// +// The ScaledPixelBuffer class allows to scale the image data +// from the source buffer to destination buffer using bilinear +// interpolation. + +#include <rdr/types.h> +#include <rfb/Rect.h> + +using namespace rdr; + +namespace rfb { + + class ScaledPixelBuffer { + public: + ScaledPixelBuffer(U8 **data, int width, int height, int scale); + ScaledPixelBuffer(); + virtual ~ScaledPixelBuffer(); + + // Get width, height, number of pixels and scale + int width() const { return scaled_width; } + int height() const { return scaled_height; } + int area() const { return scaled_width * scaled_height; } + int getScale() const { return scale; } + + // 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, scaled_width, scaled_height); } + Rect getRect(const Point& pos) const { + return Rect(pos, pos.translate(Point(scaled_width, scaled_height))); + } + + // Set the new source buffer and its parameters + void setSourceBuffer(U8 **src_data, int w, int h); + + // Set the new scale, in percent + virtual void setScale(int scale); + + // Scale rect from the source image buffer to the destination buffer + // using bilinear interpolation + virtual void scaleRect(const Rect& r); + + // Calculate the scaled image rectangle which depend on the source + // image rectangle. + inline Rect calculateScaleBoundary(const Rect& r); + + protected: + + // Calculate the scaled buffer size depending on the source buffer + // parameters (width, height, pixel format) + void calculateScaledBufferSize(); + + // Recreate the scaled pixel buffer + virtual void recreateScaledBuffer(); + + int src_width; + int src_height; + int scaled_width; + int scaled_height; + int bpp; + int scale; + double scale_ratio; + U8 **src_data; + U8 *scaled_data; + }; + +}; diff --git a/common/rfb/ServerCore.cxx b/common/rfb/ServerCore.cxx new file mode 100644 index 00000000..750daae2 --- /dev/null +++ b/common/rfb/ServerCore.cxx @@ -0,0 +1,94 @@ +/* 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. + */ + +// -=- ServerCore.cxx + +// This header will define the Server interface, from which ServerMT and +// ServerST will be derived. + +#include <string.h> +#include <rfb/util.h> +#include <rfb/ServerCore.h> + +rfb::IntParameter rfb::Server::idleTimeout +("IdleTimeout", + "The number of seconds after which an idle VNC connection will be dropped " + "(zero means no timeout)", + 0, 0); +rfb::IntParameter rfb::Server::maxDisconnectionTime +("MaxDisconnectionTime", + "Terminate when no client has been connected for s seconds", + 0, 0); +rfb::IntParameter rfb::Server::maxConnectionTime +("MaxConnectionTime", + "Terminate when a client has been connected for s seconds", + 0, 0); +rfb::IntParameter rfb::Server::maxIdleTime +("MaxIdleTime", + "Terminate after s seconds of user inactivity", + 0, 0); +rfb::IntParameter rfb::Server::clientWaitTimeMillis +("ClientWaitTimeMillis", + "The number of milliseconds to wait for a client which is no longer " + "responding", + 20000, 0); +rfb::BoolParameter rfb::Server::compareFB +("CompareFB", + "Perform pixel comparison on framebuffer to reduce unnecessary updates", + true); +rfb::BoolParameter rfb::Server::protocol3_3 +("Protocol3.3", + "Always use protocol version 3.3 for backwards compatibility with " + "badly-behaved clients", + false); +rfb::BoolParameter rfb::Server::alwaysShared +("AlwaysShared", + "Always treat incoming connections as shared, regardless of the client-" + "specified setting", + false); +rfb::BoolParameter rfb::Server::neverShared +("NeverShared", + "Never treat incoming connections as shared, regardless of the client-" + "specified setting", + false); +rfb::BoolParameter rfb::Server::disconnectClients +("DisconnectClients", + "Disconnect existing clients if an incoming connection is non-shared. " + "If combined with NeverShared then new connections will be refused " + "while there is a client active", + true); +rfb::BoolParameter rfb::Server::acceptKeyEvents +("AcceptKeyEvents", + "Accept key press and release events from clients.", + true); +rfb::BoolParameter rfb::Server::acceptPointerEvents +("AcceptPointerEvents", + "Accept pointer press and release events from clients.", + true); +rfb::BoolParameter rfb::Server::acceptCutText +("AcceptCutText", + "Accept clipboard updates from clients.", + true); +rfb::BoolParameter rfb::Server::sendCutText +("SendCutText", + "Send clipboard changes to clients.", + true); +rfb::BoolParameter rfb::Server::queryConnect +("QueryConnect", + "Prompt the local user to accept or reject incoming connections.", + false); diff --git a/common/rfb/ServerCore.h b/common/rfb/ServerCore.h new file mode 100644 index 00000000..68d7b74b --- /dev/null +++ b/common/rfb/ServerCore.h @@ -0,0 +1,56 @@ +/* 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. + */ + +// -=- ServerCore.h + +// This header will define the Server interface, from which ServerMT and +// ServerST will be derived. + +#ifndef __RFB_SERVER_CORE_H__ +#define __RFB_SERVER_CORE_H__ + +#include <rfb/Configuration.h> +#include <rfb/util.h> + +namespace rfb { + + class Server { + public: + + static IntParameter idleTimeout; + static IntParameter maxDisconnectionTime; + static IntParameter maxConnectionTime; + static IntParameter maxIdleTime; + static IntParameter clientWaitTimeMillis; + static BoolParameter compareFB; + static BoolParameter protocol3_3; + static BoolParameter alwaysShared; + static BoolParameter neverShared; + static BoolParameter disconnectClients; + static BoolParameter acceptKeyEvents; + static BoolParameter acceptPointerEvents; + static BoolParameter acceptCutText; + static BoolParameter sendCutText; + static BoolParameter queryConnect; + + }; + +}; + +#endif // __RFB_SERVER_CORE_H__ + diff --git a/common/rfb/Threading.h b/common/rfb/Threading.h new file mode 100644 index 00000000..66b3aa0f --- /dev/null +++ b/common/rfb/Threading.h @@ -0,0 +1,31 @@ +/* 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. + */ + +// -=- Threading.h +// General purpose threading interface. +// If the current platform supports threading then __RFB_THREADING_IMPL +// will be defined after this header has been included. + +#ifndef __RFB_THREADING_H__ +#define __RFB_THREADING_H__ + +#ifdef WIN32 +#include <rfb_win32/Threading.h> +#endif + +#endif // __RFB_THREADING_H__ diff --git a/common/rfb/TightDecoder.cxx b/common/rfb/TightDecoder.cxx new file mode 100644 index 00000000..9f8c505e --- /dev/null +++ b/common/rfb/TightDecoder.cxx @@ -0,0 +1,159 @@ +/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved. + * Copyright (C) 2004-2005 Cendio AB. 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/CMsgReader.h> +#include <rfb/CMsgHandler.h> +#include <rfb/TightDecoder.h> +#include <stdio.h> /* jpeglib.h needs FILE */ +extern "C" { +#include <jpeglib.h> +} + +using namespace rfb; + +#define RGB_TO_PIXEL(r,g,b) \ + (((PIXEL_T)(r) & myFormat.redMax) << myFormat.redShift | \ + ((PIXEL_T)(g) & myFormat.greenMax) << myFormat.greenShift | \ + ((PIXEL_T)(b) & myFormat.blueMax) << myFormat.blueShift) + +#define RGB24_TO_PIXEL(r,g,b) \ + ((((PIXEL_T)(r) & 0xFF) * myFormat.redMax + 127) / 255 \ + << myFormat.redShift | \ + (((PIXEL_T)(g) & 0xFF) * myFormat.greenMax + 127) / 255 \ + << myFormat.greenShift | \ + (((PIXEL_T)(b) & 0xFF) * myFormat.blueMax + 127) / 255 \ + << myFormat.blueShift) + +#define RGB24_TO_PIXEL32(r,g,b) \ + (((rdr::U32)(r) & 0xFF) << myFormat.redShift | \ + ((rdr::U32)(g) & 0xFF) << myFormat.greenShift | \ + ((rdr::U32)(b) & 0xFF) << myFormat.blueShift) + +#define TIGHT_MAX_WIDTH 2048 + +static void JpegSetSrcManager(j_decompress_ptr cinfo, char *compressedData, + int compressedLen); +static bool jpegError; + +#define EXTRA_ARGS CMsgHandler* handler +#define FILL_RECT(r, p) handler->fillRect(r, p) +#define IMAGE_RECT(r, p) handler->imageRect(r, p) +#define BPP 8 +#include <rfb/tightDecode.h> +#undef BPP +#define BPP 16 +#include <rfb/tightDecode.h> +#undef BPP +#define BPP 32 +#include <rfb/tightDecode.h> +#undef BPP + +Decoder* TightDecoder::create(CMsgReader* reader) +{ + return new TightDecoder(reader); +} + +TightDecoder::TightDecoder(CMsgReader* reader_) : reader(reader_) +{ +} + +TightDecoder::~TightDecoder() +{ +} + +void TightDecoder::readRect(const Rect& r, CMsgHandler* handler) +{ + rdr::InStream* is = reader->getInStream(); + /* Uncompressed RGB24 JPEG data, before translated, can be up to 3 + times larger, if VNC bpp is 8. */ + rdr::U8* buf = reader->getImageBuf(r.area()*3); + switch (reader->bpp()) { + case 8: + tightDecode8 (r, is, zis, (rdr::U8*) buf, handler); break; + case 16: + tightDecode16(r, is, zis, (rdr::U16*)buf, handler); break; + case 32: + tightDecode32(r, is, zis, (rdr::U32*)buf, handler); break; + } +} + + +// +// A "Source manager" for the JPEG library. +// + +static struct jpeg_source_mgr jpegSrcManager; +static JOCTET *jpegBufferPtr; +static size_t jpegBufferLen; + +static void JpegInitSource(j_decompress_ptr cinfo); +static boolean JpegFillInputBuffer(j_decompress_ptr cinfo); +static void JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes); +static void JpegTermSource(j_decompress_ptr cinfo); + +static void +JpegInitSource(j_decompress_ptr cinfo) +{ + jpegError = false; +} + +static boolean +JpegFillInputBuffer(j_decompress_ptr cinfo) +{ + jpegError = true; + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr; + + return TRUE; +} + +static void +JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes) +{ + if (num_bytes < 0 || (size_t)num_bytes > jpegSrcManager.bytes_in_buffer) { + jpegError = true; + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr; + } else { + jpegSrcManager.next_input_byte += (size_t) num_bytes; + jpegSrcManager.bytes_in_buffer -= (size_t) num_bytes; + } +} + +static void +JpegTermSource(j_decompress_ptr cinfo) +{ + /* No work necessary here. */ +} + +static void +JpegSetSrcManager(j_decompress_ptr cinfo, char *compressedData, int compressedLen) +{ + jpegBufferPtr = (JOCTET *)compressedData; + jpegBufferLen = (size_t)compressedLen; + + jpegSrcManager.init_source = JpegInitSource; + jpegSrcManager.fill_input_buffer = JpegFillInputBuffer; + jpegSrcManager.skip_input_data = JpegSkipInputData; + jpegSrcManager.resync_to_restart = jpeg_resync_to_restart; + jpegSrcManager.term_source = JpegTermSource; + jpegSrcManager.next_input_byte = jpegBufferPtr; + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + + cinfo->src = &jpegSrcManager; +} diff --git a/common/rfb/TightDecoder.h b/common/rfb/TightDecoder.h new file mode 100644 index 00000000..1047b374 --- /dev/null +++ b/common/rfb/TightDecoder.h @@ -0,0 +1,49 @@ +/* Copyright (C) 2000-2003 Constantin Kaplinsky. 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_TIGHTDECODER_H__ +#define __RFB_TIGHTDECODER_H__ + +#include <rdr/ZlibInStream.h> +#include <rfb/Decoder.h> + +namespace rfb { + + class TightDecoder : public Decoder { + public: + static Decoder* create(CMsgReader* reader); + virtual void readRect(const Rect& r, CMsgHandler* handler); + virtual ~TightDecoder(); + private: + TightDecoder(CMsgReader* reader); + CMsgReader* reader; + rdr::ZlibInStream zis[4]; + }; + + // Compression control + const unsigned int rfbTightExplicitFilter = 0x04; + const unsigned int rfbTightFill = 0x08; + const unsigned int rfbTightJpeg = 0x09; + const unsigned int rfbTightMaxSubencoding = 0x09; + + // Filters to improve compression efficiency + const unsigned int rfbTightFilterCopy = 0x00; + const unsigned int rfbTightFilterPalette = 0x01; + const unsigned int rfbTightFilterGradient = 0x02; +} + +#endif diff --git a/common/rfb/TightEncoder.cxx b/common/rfb/TightEncoder.cxx new file mode 100644 index 00000000..e89a5609 --- /dev/null +++ b/common/rfb/TightEncoder.cxx @@ -0,0 +1,193 @@ +/* Copyright (C) 2000-2003 Constantin Kaplinsky. 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/OutStream.h> +#include <rfb/ImageGetter.h> +#include <rfb/encodings.h> +#include <rfb/ConnParams.h> +#include <rfb/SMsgWriter.h> +#include <rfb/TightEncoder.h> + +using namespace rfb; + +// Minimum amount of data to be compressed. This value should not be +// changed, doing so will break compatibility with existing clients. +#define TIGHT_MIN_TO_COMPRESS 12 + +// Adjustable parameters. +// FIXME: Get rid of #defines +#define TIGHT_JPEG_MIN_RECT_SIZE 1024 +#define TIGHT_DETECT_MIN_WIDTH 8 +#define TIGHT_DETECT_MIN_HEIGHT 8 + +// +// Compression level stuff. The following array contains various +// encoder parameters for each of 10 compression levels (0..9). +// Last three parameters correspond to JPEG quality levels (0..9). +// +// NOTE: s_conf[9].maxRectSize should be >= s_conf[i].maxRectSize, +// where i in [0..8]. RequiredBuffSize() method depends on this. +// FIXME: Is this comment obsolete? +// + +const TIGHT_CONF TightEncoder::conf[10] = { + { 512, 32, 6, 0, 0, 0, 4, 5 }, + { 2048, 64, 6, 1, 1, 1, 8, 10 }, + { 4096, 128, 8, 3, 3, 2, 24, 15 }, + { 8192, 256, 12, 5, 5, 2, 32, 25 }, + { 16384, 512, 12, 6, 7, 3, 32, 37 }, + { 32768, 512, 12, 7, 8, 4, 32, 50 }, + { 65536, 1024, 16, 7, 8, 5, 32, 60 }, + { 65536, 1024, 16, 8, 9, 6, 64, 70 }, + { 65536, 2048, 24, 9, 9, 7, 64, 75 }, + { 65536, 2048, 32, 9, 9, 9, 96, 80 } +}; +const int TightEncoder::defaultCompressLevel = 6; + +// FIXME: Not good to mirror TightEncoder's members here. +static const TIGHT_CONF* s_pconf; +static const TIGHT_CONF* s_pjconf; + +// +// Including BPP-dependent implementation of the encoder. +// + +#define EXTRA_ARGS ImageGetter* ig +#define GET_IMAGE_INTO_BUF(r,buf) ig->getImage(buf, r); +#define BPP 8 +#include <rfb/tightEncode.h> +#undef BPP +#define BPP 16 +#include <rfb/tightEncode.h> +#undef BPP +#define BPP 32 +#include <rfb/tightEncode.h> +#undef BPP + +Encoder* TightEncoder::create(SMsgWriter* writer) +{ + return new TightEncoder(writer); +} + +TightEncoder::TightEncoder(SMsgWriter* writer_) : writer(writer_) +{ + setCompressLevel(defaultCompressLevel); + setQualityLevel(-1); +} + +TightEncoder::~TightEncoder() +{ +} + +void TightEncoder::setCompressLevel(int level) +{ + if (level >= 0 && level <= 9) { + pconf = &conf[level]; + } else { + pconf = &conf[defaultCompressLevel]; + } +} + +void TightEncoder::setQualityLevel(int level) +{ + if (level >= 0 && level <= 9) { + pjconf = &conf[level]; + } else { + pjconf = NULL; + } +} + +int TightEncoder::getNumRects(const Rect &r) +{ + const unsigned int w = r.width(); + const unsigned int h = r.height(); + + // Will this rectangle split into subrects? + bool rectTooBig = w > pconf->maxRectWidth || w * h > pconf->maxRectSize; + if (!rectTooBig) + return 1; + + // Compute max sub-rectangle size. + const unsigned int subrectMaxWidth = + (w > pconf->maxRectWidth) ? pconf->maxRectWidth : w; + const unsigned int subrectMaxHeight = + pconf->maxRectSize / subrectMaxWidth; + + // Return the number of subrects. + return (((w - 1) / pconf->maxRectWidth + 1) * + ((h - 1) / subrectMaxHeight + 1)); +} + +bool TightEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual) +{ + // Shortcuts to rectangle coordinates and dimensions. + const int x = r.tl.x; + const int y = r.tl.y; + const unsigned int w = r.width(); + const unsigned int h = r.height(); + + // Copy members of current TightEncoder instance to static variables. + s_pconf = pconf; + s_pjconf = pjconf; + + // Encode small rects as is. + bool rectTooBig = w > pconf->maxRectWidth || w * h > pconf->maxRectSize; + if (!rectTooBig) { + writeSubrect(r, ig); + return true; + } + + // Compute max sub-rectangle size. + const unsigned int subrectMaxWidth = + (w > pconf->maxRectWidth) ? pconf->maxRectWidth : w; + const unsigned int subrectMaxHeight = + pconf->maxRectSize / subrectMaxWidth; + + // Split big rects into separately encoded subrects. + Rect sr; + unsigned int dx, dy, sw, sh; + for (dy = 0; dy < h; dy += subrectMaxHeight) { + for (dx = 0; dx < w; dx += pconf->maxRectWidth) { + sw = (dx + pconf->maxRectWidth < w) ? pconf->maxRectWidth : w - dx; + sh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy; + sr.setXYWH(x + dx, y + dy, sw, sh); + writeSubrect(sr, ig); + } + } + return true; +} + +void TightEncoder::writeSubrect(const Rect& r, ImageGetter* ig) +{ + rdr::U8* imageBuf = writer->getImageBuf(r.area()); + ConnParams* cp = writer->getConnParams(); + mos.clear(); + + switch (writer->bpp()) { + case 8: + tightEncode8(r, &mos, zos, imageBuf, cp, ig); break; + case 16: + tightEncode16(r, &mos, zos, imageBuf, cp, ig); break; + case 32: + tightEncode32(r, &mos, zos, imageBuf, cp, ig); break; + } + + writer->startRect(r, encodingTight); + rdr::OutStream* os = writer->getOutStream(); + os->writeBytes(mos.data(), mos.length()); + writer->endRect(); +} diff --git a/common/rfb/TightEncoder.h b/common/rfb/TightEncoder.h new file mode 100644 index 00000000..9c11eaff --- /dev/null +++ b/common/rfb/TightEncoder.h @@ -0,0 +1,78 @@ +/* Copyright (C) 2000-2003 Constantin Kaplinsky. 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_TIGHTENCODER_H__ +#define __RFB_TIGHTENCODER_H__ + +#include <rdr/MemOutStream.h> +#include <rdr/ZlibOutStream.h> +#include <rfb/Encoder.h> + +// FIXME: Check if specifying extern "C" is really necessary. +#include <stdio.h> +extern "C" { +#include "jpeg/jpeglib.h" +} + +namespace rfb { + + struct TIGHT_CONF { + unsigned int maxRectSize, maxRectWidth; + unsigned int monoMinRectSize; + int idxZlibLevel, monoZlibLevel, rawZlibLevel; + int idxMaxColorsDivisor; + int jpegQuality; + }; + + // + // Compression level stuff. The following array contains various + // encoder parameters for each of 10 compression levels (0..9). + // Last three parameters correspond to JPEG quality levels (0..9). + // + // NOTE: s_conf[9].maxRectSize should be >= s_conf[i].maxRectSize, + // where i in [0..8]. RequiredBuffSize() method depends on this. + // FIXME: Is this comment obsolete? + // + + + class TightEncoder : public Encoder { + public: + static Encoder* create(SMsgWriter* writer); + virtual void setCompressLevel(int level); + virtual void setQualityLevel(int level); + virtual int getNumRects(const Rect &r); + virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual); + virtual ~TightEncoder(); + + private: + TightEncoder(SMsgWriter* writer); + void writeSubrect(const Rect& r, ImageGetter* ig); + + SMsgWriter* writer; + rdr::MemOutStream mos; + rdr::ZlibOutStream zos[4]; + + static const int defaultCompressLevel; + static const TIGHT_CONF conf[]; + + const TIGHT_CONF* pconf; + const TIGHT_CONF* pjconf; + }; + +} + +#endif diff --git a/common/rfb/TightPalette.cxx b/common/rfb/TightPalette.cxx new file mode 100644 index 00000000..c4ed04e4 --- /dev/null +++ b/common/rfb/TightPalette.cxx @@ -0,0 +1,110 @@ +/* Copyright (C) 2000-2005 Constantin Kaplinsky. 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. + */ + +// +// TightPalette class implementation. +// + +#include <rfb/TightPalette.h> + +using namespace rfb; + +TightPalette::TightPalette(int maxColors) +{ + setMaxColors(maxColors); + reset(); +} + +void TightPalette::reset() +{ + m_numColors = 0; + memset(m_hash, 0, 256 * sizeof(TightColorList *)); +} + +void TightPalette::setMaxColors(int maxColors) +{ + m_maxColors = maxColors; + if (m_maxColors < 0) { + m_maxColors = 0; + } else if (m_maxColors > 254) { + m_maxColors = 254; + } +} + +int TightPalette::insert(rdr::U32 rgb, int numPixels) +{ + TightColorList *pnode; + TightColorList *prev_pnode = NULL; + int hash_key, idx, new_idx, count; + + hash_key = hashFunc(rgb); + + pnode = m_hash[hash_key]; + + while (pnode != NULL) { + if (pnode->rgb == rgb) { + // Such palette entry already exists. + new_idx = idx = pnode->idx; + count = m_entry[idx].numPixels + numPixels; + if (new_idx && m_entry[new_idx-1].numPixels < count) { + do { + m_entry[new_idx] = m_entry[new_idx-1]; + m_entry[new_idx].listNode->idx = new_idx; + new_idx--; + } + while (new_idx && m_entry[new_idx-1].numPixels < count); + + m_entry[new_idx].listNode = pnode; + pnode->idx = new_idx; + } + m_entry[new_idx].numPixels = count; + return m_numColors; + } + prev_pnode = pnode; + pnode = pnode->next; + } + + // Check if the palette is full. + if (m_numColors == 256 || m_numColors == m_maxColors) { + m_numColors = 0; + return 0; + } + + // Move palette entries with lesser pixel counts. + for ( idx = m_numColors; + idx > 0 && m_entry[idx-1].numPixels < numPixels; + idx-- ) { + m_entry[idx] = m_entry[idx-1]; + m_entry[idx].listNode->idx = idx; + } + + // Add new palette entry into the freed slot. + pnode = &m_list[m_numColors]; + if (prev_pnode != NULL) { + prev_pnode->next = pnode; + } else { + m_hash[hash_key] = pnode; + } + pnode->next = NULL; + pnode->idx = idx; + pnode->rgb = rgb; + m_entry[idx].listNode = pnode; + m_entry[idx].numPixels = numPixels; + + return ++m_numColors; +} diff --git a/common/rfb/TightPalette.h b/common/rfb/TightPalette.h new file mode 100644 index 00000000..2f6448ea --- /dev/null +++ b/common/rfb/TightPalette.h @@ -0,0 +1,127 @@ +/* Copyright (C) 2000-2005 Constantin Kaplinsky. 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. + */ + +// +// TightPalette class is a container for ordered color values. Colors +// are keys in a hash where values are frequency counts. Also, there +// is a list where colors are always sorted by these counts (more +// frequent first). +// + +#ifndef __RFB_TIGHTPALETTE_H__ +#define __RFB_TIGHTPALETTE_H__ + +#include <string.h> +#include <rdr/types.h> + +namespace rfb { + + struct TightColorList { + TightColorList *next; + int idx; + rdr::U32 rgb; + }; + + struct TightPaletteEntry { + TightColorList *listNode; + int numPixels; + }; + + class TightPalette { + + protected: + + // FIXME: Bigger hash table? Better hash function? + inline static int hashFunc(rdr::U32 rgb) { + return (rgb ^ (rgb >> 13)) & 0xFF; + } + + public: + + TightPalette(int maxColors = 254); + + // + // Re-initialize the object. This does not change maximum number + // of colors. + // + void reset(); + + // + // Set limit on the number of colors in the palette. Note that + // this value cannot exceed 254. + // + void setMaxColors(int maxColors); + + // + // Insert new color into the palette, or increment its counter if + // the color is already there. Returns new number of colors, or + // zero if the palette is full. If the palette becomes full, it + // reports zero colors and cannot be used any more without calling + // reset(). + // + int insert(rdr::U32 rgb, int numPixels); + + // + // Return number of colors in the palette. + // + inline int getNumColors() const { + return m_numColors; + } + + // + // Return the color specified by its index in the palette. + // + inline rdr::U32 getEntry(int i) const { + return (i < m_numColors) ? m_entry[i].listNode->rgb : (rdr::U32)-1; + } + + // + // Return the pixel counter of the color specified by its index. + // + inline int getCount(int i) const { + return (i < m_numColors) ? m_entry[i].numPixels : 0; + } + + // + // Return the index of a specified color. + // + inline rdr::U8 getIndex(rdr::U32 rgb) const { + TightColorList *pnode = m_hash[hashFunc(rgb)]; + while (pnode != NULL) { + if (pnode->rgb == rgb) { + return (rdr::U8)pnode->idx; + } + pnode = pnode->next; + } + return 0xFF; // no such color + } + + protected: + + int m_maxColors; + int m_numColors; + + TightPaletteEntry m_entry[256]; + TightColorList *m_hash[256]; + TightColorList m_list[256]; + + }; + +} // namespace rfb + +#endif // __RFB_TIGHTPALETTE_H__ diff --git a/common/rfb/Timer.cxx b/common/rfb/Timer.cxx new file mode 100644 index 00000000..66fd2b12 --- /dev/null +++ b/common/rfb/Timer.cxx @@ -0,0 +1,179 @@ +/* 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. + */ + +// -=- Timer.cxx + +#include <stdio.h> +#ifdef WIN32 +#include <windows.h> +#ifndef _WIN32_WCE +#include <sys/timeb.h> +#endif +#endif +#include <rfb/Timer.h> +#include <rfb/util.h> +#include <rfb/LogWriter.h> + +// XXX Lynx/OS 2.3: proto for gettimeofday() +#ifdef Lynx +#include <sys/proto.h> +#endif + +using namespace rfb; + +#ifndef __NO_DEFINE_VLOG__ +static LogWriter vlog("Timer"); +#endif + + +// Win32 does not provide gettimeofday, so we emulate it to simplify the +// Timer code. + +#ifdef _WIN32 +static void gettimeofday(struct timeval* tv, void*) +{ + LARGE_INTEGER counts, countsPerSec; + static double usecPerCount = 0.0; + + if (QueryPerformanceCounter(&counts)) { + if (usecPerCount == 0.0) { + QueryPerformanceFrequency(&countsPerSec); + usecPerCount = 1000000.0 / countsPerSec.QuadPart; + } + + LONGLONG usecs = (LONGLONG)(counts.QuadPart * usecPerCount); + tv->tv_usec = (long)(usecs % 1000000); + tv->tv_sec = (long)(usecs / 1000000); + + } else { +#ifndef _WIN32_WCE + struct timeb tb; + ftime(&tb); + tv->tv_sec = tb.time; + tv->tv_usec = tb.millitm * 1000; +#else + throw SystemException("QueryPerformanceCounter", GetLastError()); +#endif + } +} +#endif + + +// Millisecond timeout processing helper functions + +inline static timeval addMillis(timeval inTime, int millis) { + int secs = millis / 1000; + millis = millis % 1000; + inTime.tv_sec += secs; + inTime.tv_usec += millis * 1000; + if (inTime.tv_usec >= 1000000) { + inTime.tv_sec++; + inTime.tv_usec -= 1000000; + } + return inTime; +} + +inline static int diffTimeMillis(timeval later, timeval earlier) { + return ((later.tv_sec - earlier.tv_sec) * 1000) + ((later.tv_usec - earlier.tv_usec) / 1000); +} + +std::list<Timer*> Timer::pending; + +int Timer::checkTimeouts() { + if (pending.empty()) + return 0; + timeval now; + gettimeofday(&now, 0); + while (pending.front()->isBefore(now)) { + Timer* timer = pending.front(); + pending.pop_front(); + vlog.debug("handleTimeout(%p)", timer); + if (timer->cb->handleTimeout(timer)) { + timer->dueTime = addMillis(timer->dueTime, timer->timeoutMs); + if (timer->isBefore(now)) { + // Time has jumped forwards! + vlog.info("time has moved forwards!"); + timer->dueTime = addMillis(now, timer->timeoutMs); + } + insertTimer(timer); + } else if (pending.empty()) { + return 0; + } + } + return getNextTimeout(); +} + +int Timer::getNextTimeout() { + timeval now; + gettimeofday(&now, 0); + int toWait = __rfbmax(1, diffTimeMillis(pending.front()->dueTime, now)); + if (toWait > pending.front()->timeoutMs) { + if (toWait - pending.front()->timeoutMs < 1000) { + vlog.info("gettimeofday is broken..."); + return toWait; + } + // Time has jumped backwards! + vlog.info("time has moved backwards!"); + pending.front()->dueTime = now; + toWait = 1; + } + return toWait; +} + +void Timer::insertTimer(Timer* t) { + std::list<Timer*>::iterator i; + for (i=pending.begin(); i!=pending.end(); i++) { + if (t->isBefore((*i)->dueTime)) { + pending.insert(i, t); + return; + } + } + pending.push_back(t); +} + +void Timer::start(int timeoutMs_) { + timeval now; + gettimeofday(&now, 0); + stop(); + timeoutMs = timeoutMs_; + dueTime = addMillis(now, timeoutMs); + insertTimer(this); +} + +void Timer::stop() { + pending.remove(this); +} + +bool Timer::isStarted() { + std::list<Timer*>::iterator i; + for (i=pending.begin(); i!=pending.end(); i++) { + if (*i == this) + return true; + } + return false; +} + +int Timer::getTimeoutMs() { + return timeoutMs; +} + +bool Timer::isBefore(timeval other) { + return (dueTime.tv_sec < other.tv_sec) || + ((dueTime.tv_sec == other.tv_sec) && + (dueTime.tv_usec < other.tv_usec)); +} diff --git a/common/rfb/Timer.h b/common/rfb/Timer.h new file mode 100644 index 00000000..e295b826 --- /dev/null +++ b/common/rfb/Timer.h @@ -0,0 +1,102 @@ +/* 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_TIMER_H__ +#define __RFB_TIMER_H__ + +#include <list> +#ifdef WIN32 +#include <winsock2.h> +#else +#include <sys/time.h> +#endif + +namespace rfb { + + /* Timer + + Cross-platform timeout handling. The caller creates instances of Timer and passes a + Callback implementation to each. The Callback will then be called with a pointer to + the Timer instance that timed-out when the timeout occurs. + + The static methods of Timer are used by the main loop of the application both to + dispatch elapsed Timer callbacks and to determine how long to wait in select() for + the next timeout to occur. + + */ + + struct Timer { + + struct Callback { + // handleTimeout + // Passed a pointer to the Timer that has timed out. If the handler returns true + // then the Timer is reset and left running, causing another timeout after the + // appropriate interval. + // If the handler returns false then the Timer is cancelled. + virtual bool handleTimeout(Timer* t) = 0; + }; + + // checkTimeouts() + // Dispatches any elapsed Timers, and returns the number of milliseconds until the + // next Timer will timeout. + static int checkTimeouts(); + + // getNextTimeout() + // Returns the number of milliseconds until the next timeout, without dispatching + // any elapsed Timers. + static int getNextTimeout(); + + // Create a Timer with the specified callback handler + Timer(Callback* cb_) {cb = cb_;} + ~Timer() {stop();} + + // startTimer + // Starts the timer, causing a timeout after the specified number of milliseconds. + // If the timer is already active then it will be implicitly cancelled and re-started. + void start(int timeoutMs_); + + // stopTimer + // Cancels the timer. + void stop(); + + // isStarted + // Determines whether the timer is started. + bool isStarted(); + + // getTimeoutMs + // Determines the previously used timeout value, if any. + // Usually used with isStarted() to get the _current_ timeout. + int getTimeoutMs(); + + // isBefore + // Determine whether the Timer will timeout before the specified time. + bool isBefore(timeval other); + + protected: + timeval dueTime; + int timeoutMs; + Callback* cb; + + static void insertTimer(Timer* t); + // The list of currently active Timers, ordered by time left until timeout. + static std::list<Timer*> pending; + }; + +}; + +#endif diff --git a/common/rfb/TransImageGetter.cxx b/common/rfb/TransImageGetter.cxx new file mode 100644 index 00000000..82c291b6 --- /dev/null +++ b/common/rfb/TransImageGetter.cxx @@ -0,0 +1,278 @@ +/* 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 <stdlib.h> +#include <rfb/PixelFormat.h> +#include <rfb/Exception.h> +#include <rfb/ConnParams.h> +#include <rfb/SMsgWriter.h> +#include <rfb/ColourMap.h> +#include <rfb/TrueColourMap.h> +#include <rfb/PixelBuffer.h> +#include <rfb/ColourCube.h> +#include <rfb/TransImageGetter.h> + +using namespace rfb; + +const PixelFormat bgr233PF(8, 8, false, true, 7, 7, 3, 0, 3, 6); + +static void noTransFn(void* table_, + const PixelFormat& inPF, void* inPtr, int inStride, + const PixelFormat& outPF, void* outPtr, int outStride, + int width, int height) +{ + rdr::U8* ip = (rdr::U8*)inPtr; + rdr::U8* op = (rdr::U8*)outPtr; + int inStrideBytes = inStride * (inPF.bpp/8); + int outStrideBytes = outStride * (outPF.bpp/8); + int widthBytes = width * (outPF.bpp/8); + + while (height > 0) { + memcpy(op, ip, widthBytes); + ip += inStrideBytes; + op += outStrideBytes; + height--; + } +} + +#define BPPOUT 8 +#include "transInitTempl.h" +#define BPPIN 8 +#include "transTempl.h" +#undef BPPIN +#define BPPIN 16 +#include "transTempl.h" +#undef BPPIN +#define BPPIN 32 +#include "transTempl.h" +#undef BPPIN +#undef BPPOUT + +#define BPPOUT 16 +#include "transInitTempl.h" +#define BPPIN 8 +#include "transTempl.h" +#undef BPPIN +#define BPPIN 16 +#include "transTempl.h" +#undef BPPIN +#define BPPIN 32 +#include "transTempl.h" +#undef BPPIN +#undef BPPOUT + +#define BPPOUT 32 +#include "transInitTempl.h" +#define BPPIN 8 +#include "transTempl.h" +#undef BPPIN +#define BPPIN 16 +#include "transTempl.h" +#undef BPPIN +#define BPPIN 32 +#include "transTempl.h" +#undef BPPIN +#undef BPPOUT + + +// Translation functions. Note that transSimple* is only used for 8/16bpp and +// transRGB* is used for 16/32bpp + +static transFnType transSimpleFns[][3] = { + { transSimple8to8, transSimple8to16, transSimple8to32 }, + { transSimple16to8, transSimple16to16, transSimple16to32 }, +}; +static transFnType transRGBFns[][3] = { + { transRGB16to8, transRGB16to16, transRGB16to32 }, + { transRGB32to8, transRGB32to16, transRGB32to32 } +}; +static transFnType transRGBCubeFns[][3] = { + { transRGBCube16to8, transRGBCube16to16, transRGBCube16to32 }, + { transRGBCube32to8, transRGBCube32to16, transRGBCube32to32 } +}; + +// Table initialisation functions. + +typedef void (*initCMtoTCFnType)(rdr::U8** tablep, const PixelFormat& inPF, + ColourMap* cm, const PixelFormat& outPF); +typedef void (*initTCtoTCFnType)(rdr::U8** tablep, const PixelFormat& inPF, + const PixelFormat& outPF); +typedef void (*initCMtoCubeFnType)(rdr::U8** tablep, const PixelFormat& inPF, + ColourMap* cm, ColourCube* cube); +typedef void (*initTCtoCubeFnType)(rdr::U8** tablep, const PixelFormat& inPF, + ColourCube* cube); + + +static initCMtoTCFnType initSimpleCMtoTCFns[] = { + initSimpleCMtoTC8, initSimpleCMtoTC16, initSimpleCMtoTC32 +}; + +static initTCtoTCFnType initSimpleTCtoTCFns[] = { + initSimpleTCtoTC8, initSimpleTCtoTC16, initSimpleTCtoTC32 +}; + +static initCMtoCubeFnType initSimpleCMtoCubeFns[] = { + initSimpleCMtoCube8, initSimpleCMtoCube16, initSimpleCMtoCube32 +}; + +static initTCtoCubeFnType initSimpleTCtoCubeFns[] = { + initSimpleTCtoCube8, initSimpleTCtoCube16, initSimpleTCtoCube32 +}; + +static initTCtoTCFnType initRGBTCtoTCFns[] = { + initRGBTCtoTC8, initRGBTCtoTC16, initRGBTCtoTC32 +}; + +static initTCtoCubeFnType initRGBTCtoCubeFns[] = { + initRGBTCtoCube8, initRGBTCtoCube16, initRGBTCtoCube32 +}; + + +TransImageGetter::TransImageGetter(bool econ) + : economic(econ), pb(0), table(0), transFn(0), cube(0) +{ +} + +TransImageGetter::~TransImageGetter() +{ + delete [] table; +} + +void TransImageGetter::init(PixelBuffer* pb_, const PixelFormat& out, + SMsgWriter* writer, ColourCube* cube_) +{ + pb = pb_; + outPF = out; + transFn = 0; + cube = cube_; + const PixelFormat& inPF = pb->getPF(); + + if ((inPF.bpp != 8) && (inPF.bpp != 16) && (inPF.bpp != 32)) + throw Exception("TransImageGetter: bpp in not 8, 16 or 32"); + + if ((outPF.bpp != 8) && (outPF.bpp != 16) && (outPF.bpp != 32)) + throw Exception("TransImageGetter: bpp out not 8, 16 or 32"); + + if (!outPF.trueColour) { + if (outPF.bpp != 8) + throw Exception("TransImageGetter: outPF has color map but not 8bpp"); + + if (!inPF.trueColour) { + if (inPF.bpp != 8) + throw Exception("TransImageGetter: inPF has colorMap but not 8bpp"); + + // CM to CM/Cube + + if (cube) { + transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16]; + (*initSimpleCMtoCubeFns[outPF.bpp/16]) (&table, inPF, + pb->getColourMap(), cube); + } else { + transFn = noTransFn; + setColourMapEntries(0, 256, writer); + } + return; + } + + // TC to CM/Cube + + ColourCube defaultCube(6,6,6); + if (!cube) cube = &defaultCube; + + if ((inPF.bpp > 16) || (economic && (inPF.bpp == 16))) { + transFn = transRGBCubeFns[inPF.bpp/32][outPF.bpp/16]; + (*initRGBTCtoCubeFns[outPF.bpp/16]) (&table, inPF, cube); + } else { + transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16]; + (*initSimpleTCtoCubeFns[outPF.bpp/16]) (&table, inPF, cube); + } + + if (cube != &defaultCube) + return; + + if (writer) writer->writeSetColourMapEntries(0, 216, cube); + cube = 0; + return; + } + + if (inPF.equal(outPF)) { + transFn = noTransFn; + return; + } + + if (!inPF.trueColour) { + + // CM to TC + + if (inPF.bpp != 8) + throw Exception("TransImageGetter: inPF has colorMap but not 8bpp"); + transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16]; + (*initSimpleCMtoTCFns[outPF.bpp/16]) (&table, inPF, pb->getColourMap(), + outPF); + return; + } + + // TC to TC + + if ((inPF.bpp > 16) || (economic && (inPF.bpp == 16))) { + transFn = transRGBFns[inPF.bpp/32][outPF.bpp/16]; + (*initRGBTCtoTCFns[outPF.bpp/16]) (&table, inPF, outPF); + } else { + transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16]; + (*initSimpleTCtoTCFns[outPF.bpp/16]) (&table, inPF, outPF); + } +} + +void TransImageGetter::setColourMapEntries(int firstCol, int nCols, + SMsgWriter* writer) +{ + if (nCols == 0) + nCols = (1 << pb->getPF().depth) - firstCol; + if (pb->getPF().trueColour) return; // shouldn't be called in this case + + if (outPF.trueColour) { + (*initSimpleCMtoTCFns[outPF.bpp/16]) (&table, pb->getPF(), + pb->getColourMap(), outPF); + } else if (cube) { + (*initSimpleCMtoCubeFns[outPF.bpp/16]) (&table, pb->getPF(), + pb->getColourMap(), cube); + } else if (writer && pb->getColourMap()) { + writer->writeSetColourMapEntries(firstCol, nCols, pb->getColourMap()); + } +} + +void TransImageGetter::getImage(void* outPtr, const Rect& r, int outStride) +{ + if (!transFn) + throw Exception("TransImageGetter: not initialised yet"); + + int inStride; + const rdr::U8* inPtr = pb->getPixelsR(r.translate(offset.negate()), &inStride); + + if (!outStride) outStride = r.width(); + + (*transFn)(table, pb->getPF(), (void*)inPtr, inStride, + outPF, outPtr, outStride, r.width(), r.height()); +} + +void TransImageGetter::translatePixels(void* inPtr, void* outPtr, + int nPixels) const +{ + (*transFn)(table, pb->getPF(), inPtr, nPixels, + outPF, outPtr, nPixels, nPixels, 1); +} diff --git a/common/rfb/TransImageGetter.h b/common/rfb/TransImageGetter.h new file mode 100644 index 00000000..5328e6d0 --- /dev/null +++ b/common/rfb/TransImageGetter.h @@ -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. + */ +// +// TransImageGetter - class to perform translation between pixel formats, +// implementing the ImageGetter interface. +// + +#ifndef __RFB_TRANSIMAGEGETTER_H__ +#define __RFB_TRANSIMAGEGETTER_H__ + +#include <rfb/Rect.h> +#include <rfb/PixelFormat.h> +#include <rfb/ImageGetter.h> + +namespace rfb { + typedef void (*transFnType)(void* table_, + const PixelFormat& inPF, void* inPtr, + int inStride, + const PixelFormat& outPF, void* outPtr, + int outStride, int width, int height); + + class SMsgWriter; + class ColourMap; + class PixelBuffer; + class ColourCube; + + class TransImageGetter : public ImageGetter { + public: + + TransImageGetter(bool econ=false); + virtual ~TransImageGetter(); + + // init() is called to initialise the translation tables. The PixelBuffer + // argument gives the source data and format details, outPF gives the + // client's pixel format. If the client has a colour map, then the writer + // argument is used to send a SetColourMapEntries message to the client. + + void init(PixelBuffer* pb, const PixelFormat& outPF, SMsgWriter* writer=0, + ColourCube* cube=0); + + // setColourMapEntries() is called when the PixelBuffer has a colour map + // which has changed. firstColour and nColours specify which part of the + // colour map has changed. If nColours is 0, this means the rest of the + // colour map. The PixelBuffer previously passed to init() must have a + // valid ColourMap object. If the client also has a colour map, then the + // writer argument is used to send a SetColourMapEntries message to the + // client. If the client is true colour then instead we update the + // internal translation table - in this case the caller should also make + // sure that the client receives an update of the relevant parts of the + // framebuffer (the simplest thing to do is just update the whole + // framebuffer, though it is possible to be smarter than this). + + void setColourMapEntries(int firstColour, int nColours, + SMsgWriter* writer=0); + + // getImage() gets the given rectangle of data from the PixelBuffer, + // translates it into the client's pixel format and puts it in the buffer + // pointed to by the outPtr argument. The optional outStride argument can + // be used where padding is required between the output scanlines (the + // padding will be outStride-r.width() pixels). + void getImage(void* outPtr, const Rect& r, int outStride=0); + + // translatePixels() translates the given number of pixels from inPtr, + // putting it into the buffer pointed to by outPtr. The pixels at inPtr + // should be in the same format as the PixelBuffer, and the translated + // pixels will be in the format previously given by the outPF argument to + // init(). Note that this call does not use the PixelBuffer's pixel data. + void translatePixels(void* inPtr, void* outPtr, int nPixels) const; + + // setPixelBuffer() changes the pixel buffer to be used. The new pixel + // buffer MUST have the same pixel format as the old one - if not you + // should call init() instead. + void setPixelBuffer(PixelBuffer* pb_) { pb = pb_; } + + // setOffset() sets an offset which is subtracted from the coordinates of + // the rectangle given to getImage(). + void setOffset(const Point& offset_) { offset = offset_; } + + private: + bool economic; + PixelBuffer* pb; + PixelFormat outPF; + rdr::U8* table; + transFnType transFn; + ColourCube* cube; + Point offset; + }; +} +#endif diff --git a/common/rfb/TransferQueue.cxx b/common/rfb/TransferQueue.cxx new file mode 100644 index 00000000..01807524 --- /dev/null +++ b/common/rfb/TransferQueue.cxx @@ -0,0 +1,311 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- TransferQueue. + +#include <rfb/TransferQueue.h> + +using namespace rfb; + +TransferQueue::TransferQueue() +{ + m_numEntries = 0; + m_pEntries = NULL; +} + +TransferQueue::~TransferQueue() +{ + free(); +} + +void +TransferQueue::add(TransferQueue *pTQ) +{ + for (unsigned int i = 0; i < pTQ->getNumEntries(); i++) { + add(pTQ->getLocPathAt(i), pTQ->getRemPathAt(i), pTQ->getLocNameAt(i), + pTQ->getRemNameAt(i), pTQ->getSizeAt(i), pTQ->getDataAt(i), pTQ->getFlagsAt(i)); + } +} + +void +TransferQueue::add(char *pLocPath, char *pRemPath, FileInfo *pFI, unsigned int flags) +{ + char locPath[FT_FILENAME_SIZE]; + char remPath[FT_FILENAME_SIZE]; + strcpy(locPath, pLocPath); + strcpy(remPath, pRemPath); + + for (unsigned int i = 0; i < pFI->getNumEntries(); i++) { + add(locPath, remPath, pFI->getNameAt(i), pFI->getNameAt(i), + pFI->getSizeAt(i), pFI->getDataAt(i), (pFI->getFlagsAt(i) | flags)); + } +} + +void +TransferQueue::add(char *pLocPath, char *pRemPath, char *pLocName, char *pRemName, + unsigned int size, unsigned int data, unsigned int flags) +{ + FILEINFOEX *pTemporary = new FILEINFOEX[m_numEntries + 1]; + if (m_numEntries != 0) + memcpy(pTemporary, m_pEntries, m_numEntries * sizeof(FILEINFOEX)); + + strcpy(pTemporary[m_numEntries].locPath, pLocPath); + strcpy(pTemporary[m_numEntries].locName, pLocName); + strcpy(pTemporary[m_numEntries].remPath, pRemPath); + strcpy(pTemporary[m_numEntries].remName, pRemName); + + pTemporary[m_numEntries].info.size = size; + pTemporary[m_numEntries].info.data = data; + pTemporary[m_numEntries].info.flags = flags; + + if (m_pEntries != NULL) { + delete [] m_pEntries; + m_pEntries = NULL; + } + + m_pEntries = pTemporary; + pTemporary = NULL; + m_numEntries++; +} + +char * +TransferQueue::getLocPathAt(unsigned int number) +{ + if ((number >= 0) && (number < m_numEntries)) { + return m_pEntries[number].locPath; + } + return NULL; +} + +char * +TransferQueue::getLocNameAt(unsigned int number) +{ + if ((number >= 0) && (number < m_numEntries)) { + return m_pEntries[number].locName; + } + return NULL; +} + +char * +TransferQueue::getRemPathAt(unsigned int number) +{ + if ((number >= 0) && (number < m_numEntries)) { + return m_pEntries[number].remPath; + } + return NULL; +} + +char * +TransferQueue::getRemNameAt(unsigned int number) +{ + if ((number >= 0) && (number < m_numEntries)) { + return m_pEntries[number].remName; + } + return NULL; +} + +char * +TransferQueue::getFullLocPathAt(unsigned int number) +{ + if ((number >= 0) && (number < m_numEntries)) { + sprintf(m_szFullLocPath, "%s\\%s", getLocPathAt(number), getLocNameAt(number)); + return m_szFullLocPath; + } + return NULL; +} + +char * +TransferQueue::getFullRemPathAt(unsigned int number) +{ + if ((number >= 0) && (number < m_numEntries)) { + sprintf(m_szFullRemPath, "%s\\%s", getRemPathAt(number), getRemNameAt(number)); + return m_szFullRemPath; + } + return NULL; +} + +SIZEDATAFLAGSINFO * +TransferQueue::getSizeDataFlagsAt(unsigned int number) +{ + if ((number >= 0) && (number < m_numEntries)) { + return &m_pEntries[number].info; + } + return NULL; +} + +bool +TransferQueue::setLocPathAt(unsigned int number, char *pName) +{ + if ((number >= 0) && (number < m_numEntries)) { + strcpy(m_pEntries[number].locPath, pName); + return true; + } + return false; +} + +bool +TransferQueue::setLocNameAt(unsigned int number, char *pName) +{ + if ((number >= 0) && (number < m_numEntries)) { + strcpy(m_pEntries[number].locName, pName); + return true; + } + return false; +} + +bool +TransferQueue::setRemPathAt(unsigned int number, char *pName) +{ + if ((number >= 0) && (number < m_numEntries)) { + strcpy(m_pEntries[number].remPath, pName); + return true; + } + return false; +} + +bool +TransferQueue::setRemNameAt(unsigned int number, char *pName) +{ + if ((number >= 0) && (number < m_numEntries)) { + strcpy(m_pEntries[number].remName, pName); + return true; + } + return false; +} + +unsigned int +TransferQueue::getSizeAt(unsigned int number) +{ + if ((number >= 0) && (number < m_numEntries)) { + return m_pEntries[number].info.size; + } + return 0; +} + +unsigned int +TransferQueue::getDataAt(unsigned int number) +{ + if ((number >= 0) && (number < m_numEntries)) { + return m_pEntries[number].info.data; + } + return 0; +} + +unsigned int +TransferQueue::getFlagsAt(unsigned int number) +{ + if ((number >= 0) && (number < m_numEntries)) { + return m_pEntries[number].info.flags; + } + return 0; +} + +bool +TransferQueue::setSizeAt(unsigned int number, unsigned int value) +{ + if ((number >= 0) && (number < m_numEntries)) { + m_pEntries[number].info.size = value; + return true; + } + return false; +} + +bool +TransferQueue::setDataAt(unsigned int number, unsigned int value) +{ + if ((number >= 0) && (number < m_numEntries)) { + m_pEntries[number].info.data = value; + return true; + } + return false; +} + +bool +TransferQueue::setFlagsAt(unsigned int number, unsigned int value) +{ + if ((number >= 0) && (number < m_numEntries)) { + m_pEntries[number].info.flags = m_pEntries[number].info.flags | value; + return true; + } + return false; +} + +bool +TransferQueue::clearFlagAt(unsigned int number, unsigned int value) +{ + if ((number >= 0) && (number < m_numEntries)) { + m_pEntries[number].info.flags = (m_pEntries[number].info.flags & (value ^ 0xFFFFFFFF)); + return true; + } + return false; +} + +bool +TransferQueue::setFlagToAll(unsigned int flag) +{ + for (unsigned int i = 0; i < m_numEntries; i++) { + setFlagsAt(i, flag); + } + return true; +} + +bool +TransferQueue::deleteAt(unsigned int number) +{ + if ((number >= m_numEntries) || (number < 0)) return false; + + FILEINFOEX *pTemporary = new FILEINFOEX[m_numEntries - 1]; + + if (number == 0) { + memcpy(pTemporary, &m_pEntries[1], (m_numEntries - 1) * sizeof(FILEINFOEX)); + } else { + memcpy(pTemporary, m_pEntries, number * sizeof(FILEINFOEX)); + if (number != (m_numEntries - 1)) + memcpy(&pTemporary[number], &m_pEntries[number + 1], (m_numEntries - number - 1) * sizeof(FILEINFOEX)); + } + + if (m_pEntries != NULL) { + delete [] m_pEntries; + m_pEntries = NULL; + } + m_pEntries = pTemporary; + pTemporary = NULL; + m_numEntries--; + return true; +} + +unsigned int +TransferQueue::getNumEntries() +{ + return m_numEntries; +} + +void +TransferQueue::free() +{ + if (m_pEntries != NULL) { + delete [] m_pEntries; + m_pEntries = NULL; + } + m_numEntries = 0; +} diff --git a/common/rfb/TransferQueue.h b/common/rfb/TransferQueue.h new file mode 100644 index 00000000..ba748e0e --- /dev/null +++ b/common/rfb/TransferQueue.h @@ -0,0 +1,87 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + * + * TightVNC distribution homepage on the Web: http://www.tightvnc.com/ + * + */ + +// -=- TransferQueue. + +#ifndef __RFB_TRANSFERQUEUE_H__ +#define __RFB_TRANSFERQUEUE_H__ + +#include <stdlib.h> + +#include <rfb/FileInfo.h> +#include <rfb/fttypes.h> + +namespace rfb { + class TransferQueue + { + public: + TransferQueue(); + ~TransferQueue(); + + void add(TransferQueue *pTQ); + void add(char *pLocPath, char*pRemPath, FileInfo *pFI, unsigned int flags); + void add(char *pLocPath, char *pRemPath, char *pLocName, char *pRemName, + unsigned int size, unsigned int data, unsigned int flags); + + char *getLocPathAt(unsigned int number); + char *getLocNameAt(unsigned int number); + char *getRemPathAt(unsigned int number); + char *getRemNameAt(unsigned int number); + + char *getFullLocPathAt(unsigned int number); + char *getFullRemPathAt(unsigned int number); + + bool setLocPathAt(unsigned int number, char *pName); + bool setLocNameAt(unsigned int number, char *pName); + bool setRemPathAt(unsigned int number, char *pName); + bool setRemNameAt(unsigned int number, char *pName); + + unsigned int getSizeAt(unsigned int number); + unsigned int getDataAt(unsigned int number); + unsigned int getFlagsAt(unsigned int number); + + SIZEDATAFLAGSINFO * getSizeDataFlagsAt(unsigned int number); + + + bool setSizeAt(unsigned int number, unsigned int value); + bool setDataAt(unsigned int number, unsigned int value); + bool setFlagsAt(unsigned int number, unsigned int value); + bool clearFlagAt(unsigned int number, unsigned int value); + bool setFlagToAll(unsigned int flag); + + bool deleteAt(unsigned int number); + + unsigned int getNumEntries(); + + void free(); + + private: + FILEINFOEX *m_pEntries; + unsigned int m_numEntries; + + char m_szFullLocPath[FT_FILENAME_SIZE]; + char m_szFullRemPath[FT_FILENAME_SIZE]; + }; +} + +#endif // __RFB_TRANSFERQUEUE_H__ diff --git a/common/rfb/TrueColourMap.h b/common/rfb/TrueColourMap.h new file mode 100644 index 00000000..1e87fa4c --- /dev/null +++ b/common/rfb/TrueColourMap.h @@ -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_TRUECOLOURMAP_H__ +#define __RFB_TRUECOLOURMAP_H__ + +#include <rfb/ColourMap.h> + +namespace rfb { + + class TrueColourMap : public ColourMap { + public: + TrueColourMap(const PixelFormat& pf_) : pf(pf_) {} + + virtual void lookup(int i, int* r, int* g, int* b) + { + *r = (((i >> pf.redShift ) & pf.redMax) + * 65535 + pf.redMax/2) / pf.redMax; + *g = (((i >> pf.greenShift) & pf.greenMax) + * 65535 + pf.greenMax/2) / pf.greenMax; + *b = (((i >> pf.blueShift) & pf.blueMax) + * 65535 + pf.blueMax/2) / pf.blueMax; + } + private: + PixelFormat pf; + }; +} +#endif diff --git a/common/rfb/UpdateTracker.cxx b/common/rfb/UpdateTracker.cxx new file mode 100644 index 00000000..14ac49d7 --- /dev/null +++ b/common/rfb/UpdateTracker.cxx @@ -0,0 +1,156 @@ +/* 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. + */ + +// -=- rfbUpdateTracker.cpp +// +// Tracks updated regions and a region-copy event, too +// + +#include <assert.h> + +#include <rfb/UpdateTracker.h> +#include <rfb/LogWriter.h> + +using namespace rfb; + +static LogWriter vlog("UpdateTracker"); + + +// -=- ClippingUpdateTracker + +void ClippingUpdateTracker::add_changed(const Region ®ion) { + ut->add_changed(region.intersect(clipRect)); +} + +void ClippingUpdateTracker::add_copied(const Region &dest, const Point &delta) { + // Clip the destination to the display area + Region clipdest = dest.intersect(clipRect); + if (clipdest.is_empty()) return; + + // Clip the source to the screen + Region tmp = clipdest; + tmp.translate(delta.negate()); + tmp.assign_intersect(clipRect); + if (!tmp.is_empty()) { + // Translate the source back to a destination region + tmp.translate(delta); + + // Pass the copy region to the child tracker + ut->add_copied(tmp, delta); + } + + // And add any bits that we had to remove to the changed region + tmp = clipdest.subtract(tmp); + if (!tmp.is_empty()) + ut->add_changed(tmp); +} + +// SimpleUpdateTracker + +SimpleUpdateTracker::SimpleUpdateTracker(bool use_copyrect) { + copy_enabled = use_copyrect; +} + +SimpleUpdateTracker::~SimpleUpdateTracker() { +} + +void SimpleUpdateTracker::enable_copyrect(bool enable) { + if (!enable && copy_enabled) { + add_changed(copied); + copied.clear(); + } + copy_enabled=enable; +} + +void SimpleUpdateTracker::add_changed(const Region ®ion) { + changed.assign_union(region); +} + +void SimpleUpdateTracker::add_copied(const Region &dest, const Point &delta) { + // Do we support copyrect? + if (!copy_enabled) { + add_changed(dest); + return; + } + + // Is there anything to do? + if (dest.is_empty()) return; + + // Calculate whether any of this copy can be treated as a continuation + // of an earlier one + Region src = dest; + src.translate(delta.negate()); + Region overlap = src.intersect(copied); + + if (overlap.is_empty()) { + // There is no overlap + + Rect newbr = dest.get_bounding_rect(); + Rect oldbr = copied.get_bounding_rect(); + if (oldbr.area() > newbr.area()) { + // Old copyrect is (probably) bigger - use it + changed.assign_union(dest); + } else { + // New copyrect is probably bigger + // Use the new one + // But be careful not to copy stuff that still needs + // to be updated. + Region invalid_src = src.intersect(changed); + invalid_src.translate(delta); + changed.assign_union(invalid_src); + changed.assign_union(copied); + copied = dest; + copy_delta = delta; + } + return; + } + + Region invalid_src = overlap.intersect(changed); + invalid_src.translate(delta); + changed.assign_union(invalid_src); + + overlap.translate(delta); + + Region nonoverlapped_copied = dest.union_(copied).subtract(overlap); + changed.assign_union(nonoverlapped_copied); + + copied = overlap; + copy_delta = copy_delta.translate(delta); + + return; +} + +void SimpleUpdateTracker::subtract(const Region& region) { + copied.assign_subtract(region); + changed.assign_subtract(region); +} + +void SimpleUpdateTracker::getUpdateInfo(UpdateInfo* info, const Region& clip) +{ + copied.assign_subtract(changed); + info->changed = changed.intersect(clip); + info->copied = copied.intersect(clip); + info->copy_delta = copy_delta; +} + +void SimpleUpdateTracker::copyTo(UpdateTracker* to) const { + if (!copied.is_empty()) + to->add_copied(copied, copy_delta); + if (!changed.is_empty()) + to->add_changed(changed); +} diff --git a/common/rfb/UpdateTracker.h b/common/rfb/UpdateTracker.h new file mode 100644 index 00000000..5b51317a --- /dev/null +++ b/common/rfb/UpdateTracker.h @@ -0,0 +1,103 @@ +/* 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_UPDATETRACKER_INCLUDED__ +#define __RFB_UPDATETRACKER_INCLUDED__ + +#include <rfb/Rect.h> +#include <rfb/Region.h> +#include <rfb/PixelBuffer.h> + +namespace rfb { + + class UpdateInfo { + public: + Region changed; + Region copied; + Point copy_delta; + bool is_empty() const { + return copied.is_empty() && changed.is_empty(); + } + int numRects() const { + return copied.numRects() + changed.numRects(); + } + }; + + class UpdateTracker { + public: + UpdateTracker() {}; + virtual ~UpdateTracker() {}; + + virtual void add_changed(const Region ®ion) = 0; + virtual void add_copied(const Region &dest, const Point &delta) = 0; + }; + + class ClippingUpdateTracker : public UpdateTracker { + public: + ClippingUpdateTracker() : ut(0) {} + ClippingUpdateTracker(UpdateTracker* ut_, const Rect& r=Rect()) : ut(ut_), clipRect(r) {} + + void setUpdateTracker(UpdateTracker* ut_) {ut = ut_;} + void setClipRect(const Rect& cr) {clipRect = cr;} + + virtual void add_changed(const Region ®ion); + virtual void add_copied(const Region &dest, const Point &delta); + protected: + UpdateTracker* ut; + Region clipRect; + }; + + class SimpleUpdateTracker : public UpdateTracker { + public: + SimpleUpdateTracker(bool use_copyrect=true); + virtual ~SimpleUpdateTracker(); + + virtual void enable_copyrect(bool enable); + + virtual void add_changed(const Region ®ion); + virtual void add_copied(const Region &dest, const Point &delta); + virtual void subtract(const Region& region); + + // Fill the supplied UpdateInfo structure with update information + virtual void getUpdateInfo(UpdateInfo* info, const Region& cliprgn); + + // Copy the contained updates to another tracker + virtual void copyTo(UpdateTracker* to) const; + + + // Get the changed/copied regions + const Region& get_changed() const {return changed;} + const Region& get_copied() const {return copied;} + const Point& get_delta() const {return copy_delta;} + + // Move the entire update region by an offset + void translate(const Point& p) {changed.translate(p); copied.translate(p);} + + virtual bool is_empty() const {return changed.is_empty() && copied.is_empty();} + + virtual void clear() {changed.clear(); copied.clear();}; + protected: + Region changed; + Region copied; + Point copy_delta; + bool copy_enabled; + }; + +} + +#endif // __RFB_UPDATETRACKER_INCLUDED__ diff --git a/common/rfb/UserPasswdGetter.h b/common/rfb/UserPasswdGetter.h new file mode 100644 index 00000000..18b0bae3 --- /dev/null +++ b/common/rfb/UserPasswdGetter.h @@ -0,0 +1,30 @@ +/* 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_USERPASSWDGETTER_H__ +#define __RFB_USERPASSWDGETTER_H__ +namespace rfb { + class UserPasswdGetter { + public: + // getUserPasswd gets the username and password. This might involve a + // dialog, getpass(), etc. The user buffer pointer can be null, in which + // case no user name will be retrieved. The caller MUST delete [] the + // result(s). + virtual void getUserPasswd(char** user, char** password)=0; + }; +} +#endif diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx new file mode 100644 index 00000000..fe60e431 --- /dev/null +++ b/common/rfb/VNCSConnectionST.cxx @@ -0,0 +1,714 @@ +/* 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/VNCSConnectionST.h> +#include <rfb/LogWriter.h> +#include <rfb/secTypes.h> +#include <rfb/ServerCore.h> +#include <rfb/ComparingUpdateTracker.h> +#include <rfb/KeyRemapper.h> +#define XK_MISCELLANY +#define XK_XKB_KEYS +#include <rfb/keysymdef.h> + +using namespace rfb; + +static LogWriter vlog("VNCSConnST"); + +VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, + bool reverse) + : SConnection(server_->securityFactory, reverse), sock(s), server(server_), + updates(false), image_getter(server->useEconomicTranslate), + drawRenderedCursor(false), removeRenderedCursor(false), + pointerEventTime(0), accessRights(AccessDefault), + startTime(time(0)), m_pFileTransfer(0) +{ + setStreams(&sock->inStream(), &sock->outStream()); + peerEndpoint.buf = sock->getPeerEndpoint(); + VNCServerST::connectionsLog.write(1,"accepted: %s", peerEndpoint.buf); + + // Configure the socket + setSocketTimeouts(); + lastEventTime = time(0); + + // Add this client to the VNCServerST + if (server->m_pFTManager != NULL) { + SFileTransfer *pFT = server->m_pFTManager->createObject(sock); + if (pFT != NULL) { + m_pFileTransfer = pFT; + } + } + + server->clients.push_front(this); +} + + +VNCSConnectionST::~VNCSConnectionST() +{ + // If we reach here then VNCServerST is deleting us! + VNCServerST::connectionsLog.write(1,"closed: %s (%s)", + peerEndpoint.buf, + (closeReason.buf) ? closeReason.buf : ""); + + // Release any keys the client still had pressed + std::set<rdr::U32>::iterator i; + for (i=pressedKeys.begin(); i!=pressedKeys.end(); i++) + server->desktop->keyEvent(*i, false); + if (server->pointerClient == this) + server->pointerClient = 0; + + if (m_pFileTransfer) + server->m_pFTManager->destroyObject(m_pFileTransfer); + + // Remove this client from the server + server->clients.remove(this); + +} + + +// Methods called from VNCServerST + +bool VNCSConnectionST::init() +{ + try { + initialiseProtocol(); + } catch (rdr::Exception& e) { + close(e.str()); + return false; + } + return true; +} + +void VNCSConnectionST::close(const char* reason) +{ + // Log the reason for the close + if (!closeReason.buf) + closeReason.buf = strDup(reason); + else + vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason); + + if (authenticated()) { + server->lastDisconnectTime = time(0); + } + + // Just shutdown the socket and mark our state as closing. Eventually the + // calling code will call VNCServerST's removeSocket() method causing us to + // be deleted. + sock->shutdown(); + setState(RFBSTATE_CLOSING); +} + + +void VNCSConnectionST::processMessages() +{ + if (state() == RFBSTATE_CLOSING) return; + try { + // - Now set appropriate socket timeouts and process data + setSocketTimeouts(); + bool clientsReadyBefore = server->clientsReadyForUpdate(); + + while (getInStream()->checkNoWait(1)) { + processMsg(); + } + + if (!clientsReadyBefore && !requested.is_empty()) + server->desktop->framebufferUpdateRequest(); + } catch (rdr::EndOfStream&) { + close("Clean disconnection"); + } catch (rdr::Exception &e) { + close(e.str()); + } +} + +void VNCSConnectionST::writeFramebufferUpdateOrClose() +{ + try { + writeFramebufferUpdate(); + } catch(rdr::Exception &e) { + close(e.str()); + } +} + +void VNCSConnectionST::pixelBufferChange() +{ + try { + if (!authenticated()) return; + if (cp.width && cp.height && (server->pb->width() != cp.width || + server->pb->height() != cp.height)) + { + // We need to clip the next update to the new size, but also add any + // extra bits if it's bigger. If we wanted to do this exactly, something + // like the code below would do it, but at the moment we just update the + // entire new size. However, we do need to clip the renderedCursorRect + // because that might be added to updates in writeFramebufferUpdate(). + + //updates.intersect(server->pb->getRect()); + // + //if (server->pb->width() > cp.width) + // updates.add_changed(Rect(cp.width, 0, server->pb->width(), + // server->pb->height())); + //if (server->pb->height() > cp.height) + // updates.add_changed(Rect(0, cp.height, cp.width, + // server->pb->height())); + + renderedCursorRect = renderedCursorRect.intersect(server->pb->getRect()); + + cp.width = server->pb->width(); + cp.height = server->pb->height(); + if (state() == RFBSTATE_NORMAL) { + if (!writer()->writeSetDesktopSize()) { + close("Client does not support desktop resize"); + return; + } + } + } + // Just update the whole screen at the moment because we're too lazy to + // work out what's actually changed. + updates.clear(); + updates.add_changed(server->pb->getRect()); + vlog.debug("pixel buffer changed - re-initialising image getter"); + image_getter.init(server->pb, cp.pf(), writer()); + if (writer()->needFakeUpdate()) + writeFramebufferUpdate(); + } catch(rdr::Exception &e) { + close(e.str()); + } +} + +void VNCSConnectionST::setColourMapEntriesOrClose(int firstColour,int nColours) +{ + try { + setColourMapEntries(firstColour, nColours); + } catch(rdr::Exception& e) { + close(e.str()); + } +} + +void VNCSConnectionST::bell() +{ + try { + if (state() == RFBSTATE_NORMAL) writer()->writeBell(); + } catch(rdr::Exception& e) { + close(e.str()); + } +} + +void VNCSConnectionST::serverCutText(const char *str, int len) +{ + try { + if (!(accessRights & AccessCutText)) return; + if (!rfb::Server::sendCutText) return; + if (state() == RFBSTATE_NORMAL) + writer()->writeServerCutText(str, len); + } catch(rdr::Exception& e) { + close(e.str()); + } +} + +void VNCSConnectionST::setCursorOrClose() +{ + try { + setCursor(); + } catch(rdr::Exception& e) { + close(e.str()); + } +} + + +int VNCSConnectionST::checkIdleTimeout() +{ + int idleTimeout = rfb::Server::idleTimeout; + if (idleTimeout == 0) return 0; + if (state() != RFBSTATE_NORMAL && idleTimeout < 15) + idleTimeout = 15; // minimum of 15 seconds while authenticating + time_t now = time(0); + if (now < lastEventTime) { + // Someone must have set the time backwards. Set lastEventTime so that the + // idleTimeout will count from now. + vlog.info("Time has gone backwards - resetting idle timeout"); + lastEventTime = now; + } + int timeLeft = lastEventTime + idleTimeout - now; + if (timeLeft < -60) { + // Our callback is over a minute late - someone must have set the time + // forwards. Set lastEventTime so that the idleTimeout will count from + // now. + vlog.info("Time has gone forwards - resetting idle timeout"); + lastEventTime = now; + return secsToMillis(idleTimeout); + } + if (timeLeft <= 0) { + close("Idle timeout"); + return 0; + } + return secsToMillis(timeLeft); +} + +// renderedCursorChange() is called whenever the server-side rendered cursor +// changes shape or position. It ensures that the next update will clean up +// the old rendered cursor and if necessary draw the new rendered cursor. + +void VNCSConnectionST::renderedCursorChange() +{ + if (state() != RFBSTATE_NORMAL) return; + removeRenderedCursor = true; + if (needRenderedCursor()) + drawRenderedCursor = true; +} + +// needRenderedCursor() returns true if this client needs the server-side +// rendered cursor. This may be because it does not support local cursor or +// because the current cursor position has not been set by this client. +// Unfortunately we can't know for sure when the current cursor position has +// been set by this client. We guess that this is the case when the current +// cursor position is the same as the last pointer event from this client, or +// if it is a very short time since this client's last pointer event (up to a +// second). [ Ideally we should do finer-grained timing here and make the time +// configurable, but I don't think it's that important. ] + +bool VNCSConnectionST::needRenderedCursor() +{ + return (state() == RFBSTATE_NORMAL + && (!cp.supportsLocalCursor && !cp.supportsLocalXCursor + || (!server->cursorPos.equals(pointerEventPos) && + (time(0) - pointerEventTime) > 0))); +} + + +void VNCSConnectionST::approveConnectionOrClose(bool accept, + const char* reason) +{ + try { + approveConnection(accept, reason); + } catch (rdr::Exception& e) { + close(e.str()); + } +} + + + +// -=- Callbacks from SConnection + +void VNCSConnectionST::authSuccess() +{ + lastEventTime = time(0); + + server->startDesktop(); + + // - Set the connection parameters appropriately + cp.width = server->pb->width(); + cp.height = server->pb->height(); + cp.setName(server->getName()); + + // - Set the default pixel format + cp.setPF(server->pb->getPF()); + char buffer[256]; + cp.pf().print(buffer, 256); + vlog.info("Server default pixel format %s", buffer); + image_getter.init(server->pb, cp.pf(), 0); + + // - Mark the entire display as "dirty" + updates.add_changed(server->pb->getRect()); + startTime = time(0); +} + +void VNCSConnectionST::queryConnection(const char* userName) +{ + // - Authentication succeeded - clear from blacklist + CharArray name; name.buf = sock->getPeerAddress(); + server->blHosts->clearBlackmark(name.buf); + + // - Special case to provide a more useful error message + if (rfb::Server::neverShared && !rfb::Server::disconnectClients && + server->authClientCount() > 0) { + approveConnection(false, "The server is already in use"); + return; + } + + // - Does the client have the right to bypass the query? + if (reverseConnection || + !(rfb::Server::queryConnect || sock->requiresQuery()) || + (accessRights & AccessNoQuery)) + { + approveConnection(true); + return; + } + + // - Get the server to display an Accept/Reject dialog, if required + // If a dialog is displayed, the result will be PENDING, and the + // server will call approveConnection at a later time + CharArray reason; + VNCServerST::queryResult qr = server->queryConnection(sock, userName, + &reason.buf); + if (qr == VNCServerST::PENDING) + return; + + // - If server returns ACCEPT/REJECT then pass result to SConnection + approveConnection(qr == VNCServerST::ACCEPT, reason.buf); +} + +void VNCSConnectionST::clientInit(bool shared) +{ + lastEventTime = time(0); + if (rfb::Server::alwaysShared || reverseConnection) shared = true; + if (rfb::Server::neverShared) shared = false; + if (!shared) { + if (rfb::Server::disconnectClients) { + // - Close all the other connected clients + vlog.debug("non-shared connection - closing clients"); + server->closeClients("Non-shared connection requested", getSock()); + } else { + // - Refuse this connection if there are existing clients, in addition to + // this one + if (server->authClientCount() > 1) { + close("Server is already in use"); + return; + } + } + } + SConnection::clientInit(shared); +} + +void VNCSConnectionST::setPixelFormat(const PixelFormat& pf) +{ + SConnection::setPixelFormat(pf); + char buffer[256]; + pf.print(buffer, 256); + vlog.info("Client pixel format %s", buffer); + image_getter.init(server->pb, pf, writer()); + setCursor(); +} + +void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask) +{ + pointerEventTime = lastEventTime = time(0); + server->lastUserInputTime = lastEventTime; + if (!(accessRights & AccessPtrEvents)) return; + if (!rfb::Server::acceptPointerEvents) return; + if (!server->pointerClient || server->pointerClient == this) { + pointerEventPos = pos; + if (buttonMask) + server->pointerClient = this; + else + server->pointerClient = 0; + server->desktop->pointerEvent(pointerEventPos, buttonMask); + } +} + + +class VNCSConnectionSTShiftPresser { +public: + VNCSConnectionSTShiftPresser(SDesktop* desktop_) + : desktop(desktop_), pressed(false) {} + ~VNCSConnectionSTShiftPresser() { + if (pressed) { desktop->keyEvent(XK_Shift_L, false); } + } + void press() { + desktop->keyEvent(XK_Shift_L, true); + pressed = true; + } + SDesktop* desktop; + bool pressed; +}; + +// keyEvent() - record in the pressedKeys which keys were pressed. Allow +// multiple down events (for autorepeat), but only allow a single up event. +void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) { + lastEventTime = time(0); + server->lastUserInputTime = lastEventTime; + if (!(accessRights & AccessKeyEvents)) return; + if (!rfb::Server::acceptKeyEvents) return; + + // Remap the key if required + if (server->keyRemapper) + key = server->keyRemapper->remapKey(key); + + // Turn ISO_Left_Tab into shifted Tab. + VNCSConnectionSTShiftPresser shiftPresser(server->desktop); + if (key == XK_ISO_Left_Tab) { + if (pressedKeys.find(XK_Shift_L) == pressedKeys.end() && + pressedKeys.find(XK_Shift_R) == pressedKeys.end()) + shiftPresser.press(); + key = XK_Tab; + } + + if (down) { + pressedKeys.insert(key); + } else { + if (!pressedKeys.erase(key)) return; + } + server->desktop->keyEvent(key, down); +} + +void VNCSConnectionST::clientCutText(const char* str, int len) +{ + if (!(accessRights & AccessCutText)) return; + if (!rfb::Server::acceptCutText) return; + server->desktop->clientCutText(str, len); +} + +void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental) +{ + if (!(accessRights & AccessView)) return; + + SConnection::framebufferUpdateRequest(r, incremental); + + Region reqRgn(r); + requested.assign_union(reqRgn); + + if (!incremental) { + // Non-incremental update - treat as if area requested has changed + updates.add_changed(reqRgn); + server->comparer->add_changed(reqRgn); + } + + writeFramebufferUpdate(); +} + +void VNCSConnectionST::setInitialColourMap() +{ + setColourMapEntries(0, 0); +} + +// supportsLocalCursor() is called whenever the status of +// cp.supportsLocalCursor has changed. If the client does now support local +// cursor, we make sure that the old server-side rendered cursor is cleaned up +// and the cursor is sent to the client. + +void VNCSConnectionST::supportsLocalCursor() +{ + if (cp.supportsLocalCursor || cp.supportsLocalXCursor) { + removeRenderedCursor = true; + drawRenderedCursor = false; + setCursor(); + } +} + +void VNCSConnectionST::writeSetCursorCallback() +{ + if (cp.supportsLocalXCursor) { + Pixel pix0, pix1; + rdr::U8Array bitmap(server->cursor.getBitmap(&pix0, &pix1)); + if (bitmap.buf) { + // The client supports XCursor and the cursor only has two + // colors. Use the XCursor encoding. + writer()->writeSetXCursor(server->cursor.width(), + server->cursor.height(), + server->cursor.hotspot.x, + server->cursor.hotspot.y, + bitmap.buf, server->cursor.mask.buf); + return; + } else { + // More than two colors + if (!cp.supportsLocalCursor) { + // FIXME: We could reduce to two colors. + vlog.info("Unable to send multicolor cursor: RichCursor not supported by client"); + return; + } + } + } + + // Use RichCursor + rdr::U8* transData = writer()->getImageBuf(server->cursor.area()); + image_getter.translatePixels(server->cursor.data, transData, + server->cursor.area()); + writer()->writeSetCursor(server->cursor.width(), + server->cursor.height(), + server->cursor.hotspot, + transData, server->cursor.mask.buf); +} + + +void VNCSConnectionST::writeFramebufferUpdate() +{ + if (state() != RFBSTATE_NORMAL || requested.is_empty()) return; + + server->checkUpdate(); + + // If the previous position of the rendered cursor overlaps the source of the + // copy, then when the copy happens the corresponding rectangle in the + // destination will be wrong, so add it to the changed region. + + if (!updates.get_copied().is_empty() && !renderedCursorRect.is_empty()) { + Rect bogusCopiedCursor = (renderedCursorRect.translate(updates.get_delta()) + .intersect(server->pb->getRect())); + if (!updates.get_copied().intersect(bogusCopiedCursor).is_empty()) { + updates.add_changed(bogusCopiedCursor); + } + } + + // If we need to remove the old rendered cursor, just add the rectangle to + // the changed region. + + if (removeRenderedCursor) { + updates.add_changed(renderedCursorRect); + renderedCursorRect.clear(); + removeRenderedCursor = false; + } + + // Return if there is nothing to send the client. + + if (updates.is_empty() && !writer()->needFakeUpdate() && !drawRenderedCursor) + return; + + // If the client needs a server-side rendered cursor, work out the cursor + // rectangle. If it's empty then don't bother drawing it, but if it overlaps + // with the update region, we need to draw the rendered cursor regardless of + // whether it has changed. + + if (needRenderedCursor()) { + renderedCursorRect + = (server->renderedCursor.getRect(server->renderedCursorTL) + .intersect(requested.get_bounding_rect())); + + if (renderedCursorRect.is_empty()) { + drawRenderedCursor = false; + } else if (!updates.get_changed().union_(updates.get_copied()) + .intersect(renderedCursorRect).is_empty()) { + drawRenderedCursor = true; + } + + // We could remove the new cursor rect from updates here. It's not clear + // whether this is worth it. If we do remove it, then we won't draw over + // the same bit of screen twice, but we have the overhead of a more complex + // region. + + //if (drawRenderedCursor) + // updates.subtract(renderedCursorRect); + } + + UpdateInfo update; + updates.enable_copyrect(cp.useCopyRect); + updates.getUpdateInfo(&update, requested); + if (!update.is_empty() || writer()->needFakeUpdate() || drawRenderedCursor) { + // Compute the number of rectangles. Tight encoder makes the things more + // complicated as compared to the original RealVNC. + writer()->setupCurrentEncoder(); + int nRects = update.copied.numRects() + (drawRenderedCursor ? 1 : 0); + std::vector<Rect> rects; + std::vector<Rect>::const_iterator i; + update.changed.get_rects(&rects); + for (i = rects.begin(); i != rects.end(); i++) { + if (i->width() && i->height()) + nRects += writer()->getNumRects(*i); + } + + writer()->writeFramebufferUpdateStart(nRects); + Region updatedRegion; + writer()->writeRects(update, &image_getter, &updatedRegion); + updates.subtract(updatedRegion); + if (drawRenderedCursor) + writeRenderedCursorRect(); + writer()->writeFramebufferUpdateEnd(); + requested.clear(); + } +} + + +// writeRenderedCursorRect() writes a single rectangle drawing the rendered +// cursor on the client. + +void VNCSConnectionST::writeRenderedCursorRect() +{ + image_getter.setPixelBuffer(&server->renderedCursor); + image_getter.setOffset(server->renderedCursorTL); + + Rect actual; + writer()->writeRect(renderedCursorRect, &image_getter, &actual); + + image_getter.setPixelBuffer(server->pb); + image_getter.setOffset(Point(0,0)); + + drawRenderedCursor = false; +} + +void VNCSConnectionST::setColourMapEntries(int firstColour, int nColours) +{ + if (!readyForSetColourMapEntries) return; + if (server->pb->getPF().trueColour) return; + + image_getter.setColourMapEntries(firstColour, nColours, writer()); + + if (cp.pf().trueColour) { + updates.add_changed(server->pb->getRect()); + } +} + + +// setCursor() is called whenever the cursor has changed shape or pixel format. +// If the client supports local cursor then it will arrange for the cursor to +// be sent to the client. + +void VNCSConnectionST::setCursor() +{ + if (state() != RFBSTATE_NORMAL || !cp.supportsLocalCursor) return; + writer()->cursorChange(this); + if (writer()->needFakeUpdate()) + writeFramebufferUpdate(); +} + +void VNCSConnectionST::setSocketTimeouts() +{ + int timeoutms = rfb::Server::clientWaitTimeMillis; + soonestTimeout(&timeoutms, secsToMillis(rfb::Server::idleTimeout)); + if (timeoutms == 0) + timeoutms = -1; + sock->inStream().setTimeout(timeoutms); + sock->outStream().setTimeout(timeoutms); +} + +char* VNCSConnectionST::getStartTime() +{ + char* result = ctime(&startTime); + result[24] = '\0'; + return result; +} + +void VNCSConnectionST::setStatus(int status) +{ + switch (status) { + case 0: + accessRights = accessRights | AccessPtrEvents | AccessKeyEvents | AccessView; + break; + case 1: + accessRights = accessRights & !(AccessPtrEvents | AccessKeyEvents) | AccessView; + break; + case 2: + accessRights = accessRights & !(AccessPtrEvents | AccessKeyEvents | AccessView); + break; + } + framebufferUpdateRequest(server->pb->getRect(), false); +} +int VNCSConnectionST::getStatus() +{ + if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0007) + return 0; + if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0001) + return 1; + if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0000) + return 2; + return 4; +} + +bool VNCSConnectionST::processFTMsg(int type) +{ + if (m_pFileTransfer != NULL) + return m_pFileTransfer->processMessages(type); + else + return false; +} diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h new file mode 100644 index 00000000..a04296d3 --- /dev/null +++ b/common/rfb/VNCSConnectionST.h @@ -0,0 +1,176 @@ +/* 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. + */ + +// +// VNCSConnectionST is our derived class of SConnection for VNCServerST - there +// is one for each connected client. We think of VNCSConnectionST as part of +// the VNCServerST implementation, so its methods are allowed full access to +// members of VNCServerST. +// + +#ifndef __RFB_VNCSCONNECTIONST_H__ +#define __RFB_VNCSCONNECTIONST_H__ + +#include <set> +#include <rfb/SConnection.h> +#include <rfb/SMsgWriter.h> +#include <rfb/TransImageGetter.h> +#include <rfb/VNCServerST.h> +#include <rfb/SFileTransfer.h> + +namespace rfb { + class VNCSConnectionST : public SConnection, + public WriteSetCursorCallback { + public: + VNCSConnectionST(VNCServerST* server_, network::Socket* s, bool reverse); + virtual ~VNCSConnectionST(); + + // Methods called from VNCServerST. None of these methods ever knowingly + // throw an exception. + + // Unless otherwise stated, the SConnectionST may not be valid after any of + // these methods are called, since they catch exceptions and may have + // called close() which deletes the object. + + // init() must be called to initialise the protocol. If it fails it + // returns false, and close() will have been called. + bool init(); + + // close() shuts down the socket to the client and deletes the + // SConnectionST object. + void close(const char* reason); + + // processMessages() processes incoming messages from the client, invoking + // various callbacks as a result. It continues to process messages until + // reading might block. shutdown() will be called on the connection's + // Socket if an error occurs, via the close() call. + void processMessages(); + + void writeFramebufferUpdateOrClose(); + void pixelBufferChange(); + void setColourMapEntriesOrClose(int firstColour, int nColours); + void bell(); + void serverCutText(const char *str, int len); + void setCursorOrClose(); + + // checkIdleTimeout() returns the number of milliseconds left until the + // idle timeout expires. If it has expired, the connection is closed and + // zero is returned. Zero is also returned if there is no idle timeout. + int checkIdleTimeout(); + + // The following methods never throw exceptions nor do they ever delete the + // SConnectionST object. + + // renderedCursorChange() is called whenever the server-side rendered + // cursor changes shape or position. It ensures that the next update will + // clean up the old rendered cursor and if necessary draw the new rendered + // cursor. + void renderedCursorChange(); + + // needRenderedCursor() returns true if this client needs the server-side + // rendered cursor. This may be because it does not support local cursor + // or because the current cursor position has not been set by this client. + bool needRenderedCursor(); + + network::Socket* getSock() { return sock; } + bool readyForUpdate() { return !requested.is_empty(); } + void add_changed(const Region& region) { updates.add_changed(region); } + void add_copied(const Region& dest, const Point& delta) { + updates.add_copied(dest, delta); + } + + const char* getPeerEndpoint() const {return peerEndpoint.buf;} + + // approveConnectionOrClose() is called some time after + // VNCServerST::queryConnection() has returned with PENDING to accept or + // reject the connection. The accept argument should be true for + // acceptance, or false for rejection, in which case a string reason may + // also be given. + + void approveConnectionOrClose(bool accept, const char* reason); + + char* getStartTime(); + + void setStatus(int status); + int getStatus(); + + bool processFTMsg(int type); + + private: + // SConnection callbacks + + // These methods are invoked as callbacks from processMsg(). Note that + // none of these methods should call any of the above methods which may + // delete the SConnectionST object. + + virtual void authSuccess(); + virtual void queryConnection(const char* userName); + virtual void clientInit(bool shared); + virtual void setPixelFormat(const PixelFormat& pf); + virtual void pointerEvent(const Point& pos, int buttonMask); + virtual void keyEvent(rdr::U32 key, bool down); + virtual void clientCutText(const char* str, int len); + virtual void framebufferUpdateRequest(const Rect& r, bool incremental); + virtual void setInitialColourMap(); + virtual void supportsLocalCursor(); + + // setAccessRights() allows a security package to limit the access rights + // of a VNCSConnectioST to the server. These access rights are applied + // such that the actual rights granted are the minimum of the server's + // default access settings and the connection's access settings. + virtual void setAccessRights(AccessRights ar) {accessRights=ar;} + + // WriteSetCursorCallback + virtual void writeSetCursorCallback(); + + // Internal methods + + // writeFramebufferUpdate() attempts to write a framebuffer update to the + // client. + + void writeFramebufferUpdate(); + + void writeRenderedCursorRect(); + void setColourMapEntries(int firstColour, int nColours); + void setCursor(); + void setSocketTimeouts(); + + network::Socket* sock; + CharArray peerEndpoint; + VNCServerST* server; + SimpleUpdateTracker updates; + TransImageGetter image_getter; + Region requested; + bool drawRenderedCursor, removeRenderedCursor; + Rect renderedCursorRect; + + std::set<rdr::U32> pressedKeys; + + time_t lastEventTime; + time_t pointerEventTime; + Point pointerEventPos; + + AccessRights accessRights; + + CharArray closeReason; + time_t startTime; + + SFileTransfer *m_pFileTransfer; + }; +} +#endif diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h new file mode 100644 index 00000000..df0fb0e5 --- /dev/null +++ b/common/rfb/VNCServer.h @@ -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. + */ +// +// VNCServer - abstract interface implemented by the RFB library. The back-end +// code calls the relevant methods as appropriate. + +#ifndef __RFB_VNCSERVER_H__ +#define __RFB_VNCSERVER_H__ + +#include <rfb/UpdateTracker.h> +#include <rfb/SSecurity.h> + +namespace rfb { + + class VNCServer : public UpdateTracker { + public: + + // setPixelBuffer() tells the server to use the given pixel buffer. If + // this differs in size from the previous pixel buffer, this may result in + // protocol messages being sent, or clients being disconnected. + virtual void setPixelBuffer(PixelBuffer* pb) = 0; + + // setColourMapEntries() tells the server that some entries in the colour + // map have changed. The server will retrieve them via the PixelBuffer's + // ColourMap object. This may result in protocol messages being sent. + // If nColours is 0, this means the rest of the colour map. + virtual void setColourMapEntries(int firstColour=0, int nColours=0) = 0; + + // serverCutText() tells the server that the cut text has changed. This + // will normally be sent to all clients. + virtual void serverCutText(const char* str, int len) = 0; + + // bell() tells the server that it should make all clients make a bell sound. + virtual void bell() = 0; + + // clientsReadyForUpdate() returns true if there is at least one client + // waiting for an update, false if no clients are ready. + virtual bool clientsReadyForUpdate() = 0; + + // - Close all currently-connected clients, by calling + // their close() method with the supplied reason. + virtual void closeClients(const char* reason) = 0; + + // tryUpdate() causes the server to attempt to send updates to any waiting + // clients. + virtual void tryUpdate() = 0; + + // setCursor() tells the server that the cursor has changed. The + // cursorData argument contains width*height pixel values in the pixel + // buffer's format. The mask argument is a bitmask with a 1-bit meaning + // the corresponding pixel in cursorData is valid. The mask consists of + // left-to-right, top-to-bottom scanlines, where each scanline is padded to + // a whole number of bytes [(width+7)/8]. Within each byte the most + // significant bit represents the leftmost pixel, and the bytes are simply + // in left-to-right order. The server takes its own copy of the data in + // cursorData and mask. + virtual void setCursor(int width, int height, const Point& hotspot, + void* cursorData, void* mask) = 0; + + // setCursorPos() tells the server the current position of the cursor. + virtual void setCursorPos(const Point& p) = 0; + + // setSSecurityFactory() tells the server which factory to use when + // attempting to authenticate connections. + virtual void setSSecurityFactory(SSecurityFactory* f) = 0; + + // setName() tells the server what desktop title to supply to clients + virtual void setName(const char* name) = 0; + }; +} +#endif diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx new file mode 100644 index 00000000..cc18faa3 --- /dev/null +++ b/common/rfb/VNCServerST.cxx @@ -0,0 +1,531 @@ +/* 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. + */ + +// -=- Single-Threaded VNC Server implementation + + +// Note about how sockets get closed: +// +// Closing sockets to clients is non-trivial because the code which calls +// VNCServerST must explicitly know about all the sockets (so that it can block +// on them appropriately). However, VNCServerST may want to close clients for +// a number of reasons, and from a variety of entry points. The simplest is +// when processSocketEvent() is called for a client, and the remote end has +// closed its socket. A more complex reason is when processSocketEvent() is +// called for a client which has just sent a ClientInit with the shared flag +// set to false - in this case we want to close all other clients. Yet another +// reason for disconnecting clients is when the desktop size has changed as a +// result of a call to setPixelBuffer(). +// +// The responsibility for creating and deleting sockets is entirely with the +// calling code. When VNCServerST wants to close a connection to a client it +// calls the VNCSConnectionST's close() method which calls shutdown() on the +// socket. Eventually the calling code will notice that the socket has been +// shut down and call removeSocket() so that we can delete the +// VNCSConnectionST. Note that the socket must not be deleted by the calling +// code until after removeSocket() has been called. +// +// One minor complication is that we don't allocate a VNCSConnectionST object +// for a blacklisted host (since we want to minimise the resources used for +// dealing with such a connection). In order to properly implement the +// getSockets function, we must maintain a separate closingSockets list, +// otherwise blacklisted connections might be "forgotten". + + +#include <rfb/ServerCore.h> +#include <rfb/VNCServerST.h> +#include <rfb/VNCSConnectionST.h> +#include <rfb/ComparingUpdateTracker.h> +#include <rfb/SSecurityFactoryStandard.h> +#include <rfb/KeyRemapper.h> +#include <rfb/util.h> + +#include <rdr/types.h> + +using namespace rfb; + +static LogWriter slog("VNCServerST"); +LogWriter VNCServerST::connectionsLog("Connections"); +static SSecurityFactoryStandard defaultSecurityFactory; + +// +// -=- VNCServerST Implementation +// + +// -=- Constructors/Destructor + +VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_, + SSecurityFactory* sf) + : blHosts(&blacklist), desktop(desktop_), desktopStarted(false), pb(0), + m_pFTManager(0), name(strDup(name_)), pointerClient(0), comparer(0), + renderedCursorInvalid(false), + securityFactory(sf ? sf : &defaultSecurityFactory), + queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance), + useEconomicTranslate(false), + lastConnectionTime(0), disableclients(false) +{ + lastUserInputTime = lastDisconnectTime = time(0); + slog.debug("creating single-threaded server %s", name.buf); +} + +VNCServerST::~VNCServerST() +{ + slog.debug("shutting down server %s", name.buf); + + // Close any active clients, with appropriate logging & cleanup + closeClients("Server shutdown"); + + // Delete all the clients, and their sockets, and any closing sockets + // NB: Deleting a client implicitly removes it from the clients list + while (!clients.empty()) { + delete clients.front(); + } + + // Stop the desktop object if active, *only* after deleting all clients! + if (desktopStarted) { + desktopStarted = false; + desktop->stop(); + } + + delete comparer; +} + + +// SocketServer methods + +void VNCServerST::addSocket(network::Socket* sock, bool outgoing) +{ + // - Check the connection isn't black-marked + // *** do this in getSecurity instead? + CharArray address(sock->getPeerAddress()); + if (blHosts->isBlackmarked(address.buf)) { + connectionsLog.error("blacklisted: %s", address.buf); + try { + SConnection::writeConnFailedFromScratch("Too many security failures", + &sock->outStream()); + } catch (rdr::Exception&) { + } + sock->shutdown(); + closingSockets.push_back(sock); + return; + } + + if (clients.empty()) { + lastConnectionTime = time(0); + } + + VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing); + client->init(); +} + +void VNCServerST::removeSocket(network::Socket* sock) { + // - If the socket has resources allocated to it, delete them + std::list<VNCSConnectionST*>::iterator ci; + for (ci = clients.begin(); ci != clients.end(); ci++) { + if ((*ci)->getSock() == sock) { + // - Delete the per-Socket resources + delete *ci; + + // - Check that the desktop object is still required + if (authClientCount() == 0 && desktopStarted) { + slog.debug("no authenticated clients - stopping desktop"); + desktopStarted = false; + desktop->stop(); + } + return; + } + } + + // - If the Socket has no resources, it may have been a closingSocket + closingSockets.remove(sock); +} + +void VNCServerST::processSocketEvent(network::Socket* sock) +{ + // - Find the appropriate VNCSConnectionST and process the event + std::list<VNCSConnectionST*>::iterator ci; + for (ci = clients.begin(); ci != clients.end(); ci++) { + if ((*ci)->getSock() == sock) { + (*ci)->processMessages(); + return; + } + } + throw rdr::Exception("invalid Socket in VNCServerST"); +} + +int VNCServerST::checkTimeouts() +{ + int timeout = 0; + std::list<VNCSConnectionST*>::iterator ci, ci_next; + for (ci=clients.begin();ci!=clients.end();ci=ci_next) { + ci_next = ci; ci_next++; + soonestTimeout(&timeout, (*ci)->checkIdleTimeout()); + } + + int timeLeft; + time_t now; + + // Optimization: Only call time() if using any maxTime. + if (rfb::Server::maxDisconnectionTime || rfb::Server::maxConnectionTime || rfb::Server::maxIdleTime) { + now = time(0); + } + + // Check MaxDisconnectionTime + if (rfb::Server::maxDisconnectionTime && clients.empty()) { + if (now < lastDisconnectTime) { + // Someone must have set the time backwards. + slog.info("Time has gone backwards - resetting lastDisconnectTime"); + lastDisconnectTime = now; + } + timeLeft = lastDisconnectTime + rfb::Server::maxDisconnectionTime - now; + if (timeLeft < -60) { + // Someone must have set the time forwards. + slog.info("Time has gone forwards - resetting lastDisconnectTime"); + lastDisconnectTime = now; + timeLeft = rfb::Server::maxDisconnectionTime; + } + if (timeLeft <= 0) { + slog.info("MaxDisconnectionTime reached, exiting"); + exit(0); + } + soonestTimeout(&timeout, timeLeft * 1000); + } + + // Check MaxConnectionTime + if (rfb::Server::maxConnectionTime && lastConnectionTime && !clients.empty()) { + if (now < lastConnectionTime) { + // Someone must have set the time backwards. + slog.info("Time has gone backwards - resetting lastConnectionTime"); + lastConnectionTime = now; + } + timeLeft = lastConnectionTime + rfb::Server::maxConnectionTime - now; + if (timeLeft < -60) { + // Someone must have set the time forwards. + slog.info("Time has gone forwards - resetting lastConnectionTime"); + lastConnectionTime = now; + timeLeft = rfb::Server::maxConnectionTime; + } + if (timeLeft <= 0) { + slog.info("MaxConnectionTime reached, exiting"); + exit(0); + } + soonestTimeout(&timeout, timeLeft * 1000); + } + + + // Check MaxIdleTime + if (rfb::Server::maxIdleTime) { + if (now < lastUserInputTime) { + // Someone must have set the time backwards. + slog.info("Time has gone backwards - resetting lastUserInputTime"); + lastUserInputTime = now; + } + timeLeft = lastUserInputTime + rfb::Server::maxIdleTime - now; + if (timeLeft < -60) { + // Someone must have set the time forwards. + slog.info("Time has gone forwards - resetting lastUserInputTime"); + lastUserInputTime = now; + timeLeft = rfb::Server::maxIdleTime; + } + if (timeLeft <= 0) { + slog.info("MaxIdleTime reached, exiting"); + exit(0); + } + soonestTimeout(&timeout, timeLeft * 1000); + } + + return timeout; +} + + +// VNCServer methods + +void VNCServerST::setPixelBuffer(PixelBuffer* pb_) +{ + pb = pb_; + delete comparer; + comparer = 0; + + if (pb) { + comparer = new ComparingUpdateTracker(pb); + cursor.setPF(pb->getPF()); + renderedCursor.setPF(pb->getPF()); + + std::list<VNCSConnectionST*>::iterator ci, ci_next; + for (ci=clients.begin();ci!=clients.end();ci=ci_next) { + ci_next = ci; ci_next++; + (*ci)->pixelBufferChange(); + } + } else { + if (desktopStarted) + throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?"); + } +} + +void VNCServerST::setColourMapEntries(int firstColour, int nColours) +{ + std::list<VNCSConnectionST*>::iterator ci, ci_next; + for (ci = clients.begin(); ci != clients.end(); ci = ci_next) { + ci_next = ci; ci_next++; + (*ci)->setColourMapEntriesOrClose(firstColour, nColours); + } +} + +void VNCServerST::bell() +{ + std::list<VNCSConnectionST*>::iterator ci, ci_next; + for (ci = clients.begin(); ci != clients.end(); ci = ci_next) { + ci_next = ci; ci_next++; + (*ci)->bell(); + } +} + +void VNCServerST::serverCutText(const char* str, int len) +{ + std::list<VNCSConnectionST*>::iterator ci, ci_next; + for (ci = clients.begin(); ci != clients.end(); ci = ci_next) { + ci_next = ci; ci_next++; + (*ci)->serverCutText(str, len); + } +} + +void VNCServerST::add_changed(const Region& region) +{ + comparer->add_changed(region); +} + +void VNCServerST::add_copied(const Region& dest, const Point& delta) +{ + comparer->add_copied(dest, delta); +} + +bool VNCServerST::clientsReadyForUpdate() +{ + std::list<VNCSConnectionST*>::iterator ci; + for (ci = clients.begin(); ci != clients.end(); ci++) { + if ((*ci)->readyForUpdate()) + return true; + } + return false; +} + +void VNCServerST::tryUpdate() +{ + std::list<VNCSConnectionST*>::iterator ci, ci_next; + for (ci = clients.begin(); ci != clients.end(); ci = ci_next) { + ci_next = ci; ci_next++; + (*ci)->writeFramebufferUpdateOrClose(); + } +} + +void VNCServerST::setCursor(int width, int height, const Point& newHotspot, + void* data, void* mask) +{ + cursor.hotspot = newHotspot; + cursor.setSize(width, height); + memcpy(cursor.data, data, cursor.dataLen()); + memcpy(cursor.mask.buf, mask, cursor.maskLen()); + + cursor.crop(); + + renderedCursorInvalid = true; + + std::list<VNCSConnectionST*>::iterator ci, ci_next; + for (ci = clients.begin(); ci != clients.end(); ci = ci_next) { + ci_next = ci; ci_next++; + (*ci)->renderedCursorChange(); + (*ci)->setCursorOrClose(); + } +} + +void VNCServerST::setCursorPos(const Point& pos) +{ + if (!cursorPos.equals(pos)) { + cursorPos = pos; + renderedCursorInvalid = true; + std::list<VNCSConnectionST*>::iterator ci; + for (ci = clients.begin(); ci != clients.end(); ci++) + (*ci)->renderedCursorChange(); + } +} + +// Other public methods + +void VNCServerST::approveConnection(network::Socket* sock, bool accept, + const char* reason) +{ + std::list<VNCSConnectionST*>::iterator ci; + for (ci = clients.begin(); ci != clients.end(); ci++) { + if ((*ci)->getSock() == sock) { + (*ci)->approveConnectionOrClose(accept, reason); + return; + } + } +} + +void VNCServerST::closeClients(const char* reason, network::Socket* except) +{ + std::list<VNCSConnectionST*>::iterator i, next_i; + for (i=clients.begin(); i!=clients.end(); i=next_i) { + next_i = i; next_i++; + if ((*i)->getSock() != except) + (*i)->close(reason); + } +} + +void VNCServerST::getSockets(std::list<network::Socket*>* sockets) +{ + sockets->clear(); + std::list<VNCSConnectionST*>::iterator ci; + for (ci = clients.begin(); ci != clients.end(); ci++) { + sockets->push_back((*ci)->getSock()); + } + std::list<network::Socket*>::iterator si; + for (si = closingSockets.begin(); si != closingSockets.end(); si++) { + sockets->push_back(*si); + } +} + +SConnection* VNCServerST::getSConnection(network::Socket* sock) { + std::list<VNCSConnectionST*>::iterator ci; + for (ci = clients.begin(); ci != clients.end(); ci++) { + if ((*ci)->getSock() == sock) + return *ci; + } + return 0; +} + + +// -=- Internal methods + +void VNCServerST::startDesktop() +{ + if (!desktopStarted) { + slog.debug("starting desktop"); + desktop->start(this); + desktopStarted = true; + if (!pb) + throw Exception("SDesktop::start() did not set a valid PixelBuffer"); + } +} + +int VNCServerST::authClientCount() { + int count = 0; + std::list<VNCSConnectionST*>::iterator ci; + for (ci = clients.begin(); ci != clients.end(); ci++) { + if ((*ci)->authenticated()) + count++; + } + return count; +} + +inline bool VNCServerST::needRenderedCursor() +{ + std::list<VNCSConnectionST*>::iterator ci; + for (ci = clients.begin(); ci != clients.end(); ci++) + if ((*ci)->needRenderedCursor()) return true; + return false; +} + +// checkUpdate() is called just before sending an update. It checks to see +// what updates are pending and propagates them to the update tracker for each +// client. It uses the ComparingUpdateTracker's compare() method to filter out +// areas of the screen which haven't actually changed. It also checks the +// state of the (server-side) rendered cursor, if necessary rendering it again +// with the correct background. + +void VNCServerST::checkUpdate() +{ + bool renderCursor = needRenderedCursor(); + + if (comparer->is_empty() && !(renderCursor && renderedCursorInvalid)) + return; + + Region toCheck = comparer->get_changed().union_(comparer->get_copied()); + + if (renderCursor) { + Rect clippedCursorRect + = cursor.getRect(cursorTL()).intersect(pb->getRect()); + + if (!renderedCursorInvalid && (toCheck.intersect(clippedCursorRect) + .is_empty())) { + renderCursor = false; + } else { + renderedCursorTL = clippedCursorRect.tl; + renderedCursor.setSize(clippedCursorRect.width(), + clippedCursorRect.height()); + toCheck.assign_union(clippedCursorRect); + } + } + + pb->grabRegion(toCheck); + + if (rfb::Server::compareFB) + comparer->compare(); + + if (renderCursor) { + pb->getImage(renderedCursor.data, + renderedCursor.getRect(renderedCursorTL)); + renderedCursor.maskRect(cursor.getRect(cursorTL() + .subtract(renderedCursorTL)), + cursor.data, cursor.mask.buf); + renderedCursorInvalid = false; + } + + std::list<VNCSConnectionST*>::iterator ci, ci_next; + for (ci = clients.begin(); ci != clients.end(); ci = ci_next) { + ci_next = ci; ci_next++; + (*ci)->add_copied(comparer->get_copied(), comparer->get_delta()); + (*ci)->add_changed(comparer->get_changed()); + } + + comparer->clear(); +} + +void VNCServerST::getConnInfo(ListConnInfo * listConn) +{ + listConn->Clear(); + listConn->setDisable(getDisable()); + if (clients.empty()) + return; + std::list<VNCSConnectionST*>::iterator i; + for (i = clients.begin(); i != clients.end(); i++) + listConn->addInfo((void*)(*i), (*i)->getSock()->getPeerAddress(), + (*i)->getStartTime(), (*i)->getStatus()); +} + +void VNCServerST::setConnStatus(ListConnInfo* listConn) +{ + setDisable(listConn->getDisable()); + if (listConn->Empty() || clients.empty()) return; + for (listConn->iBegin(); !listConn->iEnd(); listConn->iNext()) { + VNCSConnectionST* conn = (VNCSConnectionST*)listConn->iGetConn(); + std::list<VNCSConnectionST*>::iterator i; + for (i = clients.begin(); i != clients.end(); i++) { + if ((*i) == conn) { + int status = listConn->iGetStatus(); + if (status == 3) { + (*i)->close(0); + } else { + (*i)->setStatus(status); + } + break; + } + } + } +} diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h new file mode 100644 index 00000000..bc15b7f4 --- /dev/null +++ b/common/rfb/VNCServerST.h @@ -0,0 +1,245 @@ +/* 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. + */ + +// -=- VNCServerST.h + +// Single-threaded VNCServer implementation + +#ifndef __RFB_VNCSERVERST_H__ +#define __RFB_VNCSERVERST_H__ + +#include <list> + +#include <rfb/SDesktop.h> +#include <rfb/VNCServer.h> +#include <rfb/Configuration.h> +#include <rfb/LogWriter.h> +#include <rfb/Blacklist.h> +#include <rfb/Cursor.h> +#include <network/Socket.h> +#include <rfb/ListConnInfo.h> +#include <rfb/SFileTransferManager.h> + +namespace rfb { + + class VNCSConnectionST; + class ComparingUpdateTracker; + class PixelBuffer; + class KeyRemapper; + + class VNCServerST : public VNCServer, public network::SocketServer { + public: + // -=- Constructors + + // Create a server exporting the supplied desktop. + VNCServerST(const char* name_, SDesktop* desktop_, + SSecurityFactory* securityFactory_=0); + virtual ~VNCServerST(); + + + // Methods overridden from SocketServer + + // addSocket + // Causes the server to allocate an RFB-protocol management + // structure for the socket & initialise it. + virtual void addSocket(network::Socket* sock, bool outgoing=false); + + // removeSocket + // Clean up any resources associated with the Socket + virtual void removeSocket(network::Socket* sock); + + // processSocketEvent + // Read more RFB data from the Socket. If an error occurs during + // processing then shutdown() is called on the Socket, causing + // removeSocket() to be called by the caller at a later time. + virtual void processSocketEvent(network::Socket* sock); + + // checkTimeouts + // Returns the number of milliseconds left until the next idle timeout + // expires. If any have already expired, the corresponding connections + // are closed. Zero is returned if there is no idle timeout. + virtual int checkTimeouts(); + + + // Methods overridden from VNCServer + + virtual void setPixelBuffer(PixelBuffer* pb); + virtual void setColourMapEntries(int firstColour=0, int nColours=0); + virtual void serverCutText(const char* str, int len); + virtual void add_changed(const Region ®ion); + virtual void add_copied(const Region &dest, const Point &delta); + virtual bool clientsReadyForUpdate(); + virtual void tryUpdate(); + virtual void setCursor(int width, int height, const Point& hotspot, + void* cursorData, void* mask); + virtual void setCursorPos(const Point& p); + virtual void setSSecurityFactory(SSecurityFactory* f) {securityFactory=f;} + + virtual void bell(); + + // - Close all currently-connected clients, by calling + // their close() method with the supplied reason. + virtual void closeClients(const char* reason) {closeClients(reason, 0);} + + // VNCServerST-only methods + + // closeClients() closes all RFB sessions, except the specified one (if + // any), and logs the specified reason for closure. + void closeClients(const char* reason, network::Socket* sock); + + // getSockets() gets a list of sockets. This can be used to generate an + // fd_set for calling select(). + + void getSockets(std::list<network::Socket*>* sockets); + + // getSConnection() gets the SConnection for a particular Socket. If + // the Socket is not recognised then null is returned. + + SConnection* getSConnection(network::Socket* sock); + + // getDesktopSize() returns the size of the SDesktop exported by this + // server. + Point getDesktopSize() const {return desktop->getFbSize();} + + // getName() returns the name of this VNC Server. NB: The value returned + // is the server's internal buffer which may change after any other methods + // are called - take a copy if necessary. + const char* getName() const {return name.buf;} + + // setName() specifies the desktop name that the server should provide to + // clients + void setName(const char* name_) {name.replaceBuf(strDup(name_));} + + // A QueryConnectionHandler, if supplied, is passed details of incoming + // connections to approve, reject, or query the user about. + // + // queryConnection() is called when a connection has been + // successfully authenticated. The sock and userName arguments identify + // the socket and the name of the authenticated user, if any. It should + // return ACCEPT if the connection should be accepted, REJECT if it should + // be rejected, or PENDING if a decision cannot yet be reached. If REJECT + // is returned, *reason can be set to a string describing the reason - this + // will be delete[]ed when it is finished with. If PENDING is returned, + // approveConnection() must be called some time later to accept or reject + // the connection. + enum queryResult { ACCEPT, REJECT, PENDING }; + struct QueryConnectionHandler { + virtual ~QueryConnectionHandler() {} + virtual queryResult queryConnection(network::Socket* sock, + const char* userName, + char** reason) = 0; + }; + void setQueryConnectionHandler(QueryConnectionHandler* qch) { + queryConnectionHandler = qch; + } + + // queryConnection is called as described above, and either passes the + // request on to the registered handler, or accepts the connection if + // no handler has been specified. + virtual queryResult queryConnection(network::Socket* sock, + const char* userName, + char** reason) { + return queryConnectionHandler + ? queryConnectionHandler->queryConnection(sock, userName, reason) + : ACCEPT; + } + + // approveConnection() is called by the active QueryConnectionHandler, + // some time after queryConnection() has returned with PENDING, to accept + // or reject the connection. The accept argument should be true for + // acceptance, or false for rejection, in which case a string reason may + // also be given. + void approveConnection(network::Socket* sock, bool accept, + const char* reason); + + // setBlacklist() is called to replace the VNCServerST's internal + // Blacklist instance with another instance. This allows a single + // Blacklist to be shared by multiple VNCServerST instances. + void setBlacklist(Blacklist* bl) {blHosts = bl ? bl : &blacklist;} + + // setEconomicTranslate() determines (for new connections) whether pixels + // should be translated for <=16bpp clients using a large lookup table + // (fast) or separate, smaller R, G and B tables (slower). If set to true, + // small tables are used, to save memory. + void setEconomicTranslate(bool et) { useEconomicTranslate = et; } + + // setKeyRemapper() replaces the VNCServerST's default key remapper. + // NB: A null pointer is valid here. + void setKeyRemapper(KeyRemapper* kr) { keyRemapper = kr; } + + void getConnInfo(ListConnInfo * listConn); + void setConnStatus(ListConnInfo* listConn); + + bool getDisable() { return disableclients;}; + void setDisable(bool disable) { disableclients = disable;}; + + void setFTManager(rfb::SFileTransferManager *pFTManager) { m_pFTManager = pFTManager; }; + + protected: + + friend class VNCSConnectionST; + + void startDesktop(); + + static LogWriter connectionsLog; + Blacklist blacklist; + Blacklist* blHosts; + + SDesktop* desktop; + bool desktopStarted; + PixelBuffer* pb; + + SFileTransferManager *m_pFTManager; + + CharArray name; + + std::list<VNCSConnectionST*> clients; + VNCSConnectionST* pointerClient; + std::list<network::Socket*> closingSockets; + + ComparingUpdateTracker* comparer; + + Point cursorPos; + Cursor cursor; + Point cursorTL() { return cursorPos.subtract(cursor.hotspot); } + Point renderedCursorTL; + ManagedPixelBuffer renderedCursor; + bool renderedCursorInvalid; + + // - Check how many of the clients are authenticated. + int authClientCount(); + + bool needRenderedCursor(); + void checkUpdate(); + + SSecurityFactory* securityFactory; + QueryConnectionHandler* queryConnectionHandler; + KeyRemapper* keyRemapper; + bool useEconomicTranslate; + + time_t lastUserInputTime; + time_t lastDisconnectTime; + time_t lastConnectionTime; + + bool disableclients; + }; + +}; + +#endif + diff --git a/common/rfb/ZRLEDecoder.cxx b/common/rfb/ZRLEDecoder.cxx new file mode 100644 index 00000000..b7c69129 --- /dev/null +++ b/common/rfb/ZRLEDecoder.cxx @@ -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. + */ +#include <rfb/CMsgReader.h> +#include <rfb/CMsgHandler.h> +#include <rfb/ZRLEDecoder.h> + +using namespace rfb; + +#define EXTRA_ARGS CMsgHandler* handler +#define FILL_RECT(r, p) handler->fillRect(r, p) +#define IMAGE_RECT(r, p) handler->imageRect(r, p) +#define BPP 8 +#include <rfb/zrleDecode.h> +#undef BPP +#define BPP 16 +#include <rfb/zrleDecode.h> +#undef BPP +#define BPP 32 +#include <rfb/zrleDecode.h> +#define CPIXEL 24A +#include <rfb/zrleDecode.h> +#undef CPIXEL +#define CPIXEL 24B +#include <rfb/zrleDecode.h> +#undef CPIXEL +#undef BPP + +Decoder* ZRLEDecoder::create(CMsgReader* reader) +{ + return new ZRLEDecoder(reader); +} + +ZRLEDecoder::ZRLEDecoder(CMsgReader* reader_) : reader(reader_) +{ +} + +ZRLEDecoder::~ZRLEDecoder() +{ +} + +void ZRLEDecoder::readRect(const Rect& r, CMsgHandler* handler) +{ + rdr::InStream* is = reader->getInStream(); + rdr::U8* buf = reader->getImageBuf(64 * 64 * 4); + switch (reader->bpp()) { + case 8: zrleDecode8 (r, is, &zis, (rdr::U8*) buf, handler); break; + case 16: zrleDecode16(r, is, &zis, (rdr::U16*)buf, handler); break; + case 32: + { + const rfb::PixelFormat& pf = handler->cp.pf(); + bool fitsInLS3Bytes = ((pf.redMax << pf.redShift) < (1<<24) && + (pf.greenMax << pf.greenShift) < (1<<24) && + (pf.blueMax << pf.blueShift) < (1<<24)); + + bool fitsInMS3Bytes = (pf.redShift > 7 && + pf.greenShift > 7 && + pf.blueShift > 7); + + if ((fitsInLS3Bytes && !pf.bigEndian) || + (fitsInMS3Bytes && pf.bigEndian)) + { + zrleDecode24A(r, is, &zis, (rdr::U32*)buf, handler); + } + else if ((fitsInLS3Bytes && pf.bigEndian) || + (fitsInMS3Bytes && !pf.bigEndian)) + { + zrleDecode24B(r, is, &zis, (rdr::U32*)buf, handler); + } + else + { + zrleDecode32(r, is, &zis, (rdr::U32*)buf, handler); + } + break; + } + } +} diff --git a/common/rfb/ZRLEDecoder.h b/common/rfb/ZRLEDecoder.h new file mode 100644 index 00000000..fe96c737 --- /dev/null +++ b/common/rfb/ZRLEDecoder.h @@ -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. + */ +#ifndef __RFB_ZRLEDECODER_H__ +#define __RFB_ZRLEDECODER_H__ + +#include <rdr/ZlibInStream.h> +#include <rfb/Decoder.h> + +namespace rfb { + + class ZRLEDecoder : public Decoder { + public: + static Decoder* create(CMsgReader* reader); + virtual void readRect(const Rect& r, CMsgHandler* handler); + virtual ~ZRLEDecoder(); + private: + ZRLEDecoder(CMsgReader* reader); + CMsgReader* reader; + rdr::ZlibInStream zis; + }; +} +#endif diff --git a/common/rfb/ZRLEEncoder.cxx b/common/rfb/ZRLEEncoder.cxx new file mode 100644 index 00000000..d84c4cfb --- /dev/null +++ b/common/rfb/ZRLEEncoder.cxx @@ -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. + */ +#include <rdr/OutStream.h> +#include <rfb/Exception.h> +#include <rfb/ImageGetter.h> +#include <rfb/encodings.h> +#include <rfb/ConnParams.h> +#include <rfb/SMsgWriter.h> +#include <rfb/ZRLEEncoder.h> +#include <rfb/Configuration.h> + +using namespace rfb; + +rdr::MemOutStream* ZRLEEncoder::sharedMos = 0; +int ZRLEEncoder::maxLen = 4097 * 1024; // enough for width 16384 32-bit pixels + +IntParameter zlibLevel("ZlibLevel","Zlib compression level",-1); + +#define EXTRA_ARGS ImageGetter* ig +#define GET_IMAGE_INTO_BUF(r,buf) ig->getImage(buf, r); +#define BPP 8 +#include <rfb/zrleEncode.h> +#undef BPP +#define BPP 16 +#include <rfb/zrleEncode.h> +#undef BPP +#define BPP 32 +#include <rfb/zrleEncode.h> +#define CPIXEL 24A +#include <rfb/zrleEncode.h> +#undef CPIXEL +#define CPIXEL 24B +#include <rfb/zrleEncode.h> +#undef CPIXEL +#undef BPP + +Encoder* ZRLEEncoder::create(SMsgWriter* writer) +{ + return new ZRLEEncoder(writer); +} + +ZRLEEncoder::ZRLEEncoder(SMsgWriter* writer_) + : writer(writer_), zos(0,0,zlibLevel) +{ + if (sharedMos) + mos = sharedMos; + else + mos = new rdr::MemOutStream(129*1024); +} + +ZRLEEncoder::~ZRLEEncoder() +{ + if (!sharedMos) + delete mos; +} + +bool ZRLEEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual) +{ + rdr::U8* imageBuf = writer->getImageBuf(64 * 64 * 4 + 4); + mos->clear(); + bool wroteAll = true; + *actual = r; + + switch (writer->bpp()) { + case 8: + wroteAll = zrleEncode8(r, mos, &zos, imageBuf, maxLen, actual, ig); + break; + case 16: + wroteAll = zrleEncode16(r, mos, &zos, imageBuf, maxLen, actual, ig); + break; + case 32: + { + const PixelFormat& pf = writer->getConnParams()->pf(); + + bool fitsInLS3Bytes = ((pf.redMax << pf.redShift) < (1<<24) && + (pf.greenMax << pf.greenShift) < (1<<24) && + (pf.blueMax << pf.blueShift) < (1<<24)); + + bool fitsInMS3Bytes = (pf.redShift > 7 && + pf.greenShift > 7 && + pf.blueShift > 7); + + if ((fitsInLS3Bytes && !pf.bigEndian) || + (fitsInMS3Bytes && pf.bigEndian)) + { + wroteAll = zrleEncode24A(r, mos, &zos, imageBuf, maxLen, actual, ig); + } + else if ((fitsInLS3Bytes && pf.bigEndian) || + (fitsInMS3Bytes && !pf.bigEndian)) + { + wroteAll = zrleEncode24B(r, mos, &zos, imageBuf, maxLen, actual, ig); + } + else + { + wroteAll = zrleEncode32(r, mos, &zos, imageBuf, maxLen, actual, ig); + } + break; + } + } + + writer->startRect(*actual, encodingZRLE); + rdr::OutStream* os = writer->getOutStream(); + os->writeU32(mos->length()); + os->writeBytes(mos->data(), mos->length()); + writer->endRect(); + return wroteAll; +} diff --git a/common/rfb/ZRLEEncoder.h b/common/rfb/ZRLEEncoder.h new file mode 100644 index 00000000..7768917d --- /dev/null +++ b/common/rfb/ZRLEEncoder.h @@ -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. + */ +#ifndef __RFB_ZRLEENCODER_H__ +#define __RFB_ZRLEENCODER_H__ + +#include <rdr/MemOutStream.h> +#include <rdr/ZlibOutStream.h> +#include <rfb/Encoder.h> + +namespace rfb { + + class ZRLEEncoder : public Encoder { + public: + static Encoder* create(SMsgWriter* writer); + virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual); + virtual ~ZRLEEncoder(); + + // setMaxLen() sets the maximum size in bytes of any ZRLE rectangle. This + // can be used to stop the MemOutStream from growing too large. The value + // must be large enough to allow for at least one row of ZRLE tiles. So + // for example for a screen width of 2048 32-bit pixels this is 2K*4*64 = + // 512Kbytes plus a bit of overhead (the overhead is about 1/16 of the + // width, in this example about 128 bytes). + static void setMaxLen(int m) { maxLen = m; } + + // setSharedMos() sets a MemOutStream to be shared amongst all + // ZRLEEncoders. Should be called before any ZRLEEncoders are created. + static void setSharedMos(rdr::MemOutStream* mos_) { sharedMos = mos_; } + + private: + ZRLEEncoder(SMsgWriter* writer); + SMsgWriter* writer; + rdr::ZlibOutStream zos; + rdr::MemOutStream* mos; + static rdr::MemOutStream* sharedMos; + static int maxLen; + }; +} +#endif diff --git a/common/rfb/d3des.c b/common/rfb/d3des.c new file mode 100644 index 00000000..eaca5816 --- /dev/null +++ b/common/rfb/d3des.c @@ -0,0 +1,434 @@ +/* + * This is D3DES (V5.09) by Richard Outerbridge with the double and + * triple-length support removed for use in VNC. Also the bytebit[] array + * has been reversed so that the most significant bit in each byte of the + * key is ignored, not the least significant. + * + * These changes are: + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * 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. + */ + +/* D3DES (V5.09) - + * + * A portable, public domain, version of the Data Encryption Standard. + * + * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge. + * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation + * code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis + * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau, + * for humouring me on. + * + * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge. + * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. + */ + +#include "d3des.h" + +static void scrunch(unsigned char *, unsigned long *); +static void unscrun(unsigned long *, unsigned char *); +static void desfunc(unsigned long *, unsigned long *); +static void cookey(unsigned long *); + +static unsigned long KnL[32] = { 0L }; + +static unsigned short bytebit[8] = { + 01, 02, 04, 010, 020, 040, 0100, 0200 }; + +static unsigned long bigbyte[24] = { + 0x800000L, 0x400000L, 0x200000L, 0x100000L, + 0x80000L, 0x40000L, 0x20000L, 0x10000L, + 0x8000L, 0x4000L, 0x2000L, 0x1000L, + 0x800L, 0x400L, 0x200L, 0x100L, + 0x80L, 0x40L, 0x20L, 0x10L, + 0x8L, 0x4L, 0x2L, 0x1L }; + +/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */ + +static unsigned char pc1[56] = { + 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, + 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, + 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 }; + +static unsigned char totrot[16] = { + 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 }; + +static unsigned char pc2[48] = { + 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, + 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, + 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, + 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 }; + +void deskey(key, edf) /* Thanks to James Gillogly & Phil Karn! */ +unsigned char *key; +int edf; +{ + register int i, j, l, m, n; + unsigned char pc1m[56], pcr[56]; + unsigned long kn[32]; + + for ( j = 0; j < 56; j++ ) { + l = pc1[j]; + m = l & 07; + pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0; + } + for( i = 0; i < 16; i++ ) { + if( edf == DE1 ) m = (15 - i) << 1; + else m = i << 1; + n = m + 1; + kn[m] = kn[n] = 0L; + for( j = 0; j < 28; j++ ) { + l = j + totrot[i]; + if( l < 28 ) pcr[j] = pc1m[l]; + else pcr[j] = pc1m[l - 28]; + } + for( j = 28; j < 56; j++ ) { + l = j + totrot[i]; + if( l < 56 ) pcr[j] = pc1m[l]; + else pcr[j] = pc1m[l - 28]; + } + for( j = 0; j < 24; j++ ) { + if( pcr[pc2[j]] ) kn[m] |= bigbyte[j]; + if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j]; + } + } + cookey(kn); + return; + } + +static void cookey(raw1) +register unsigned long *raw1; +{ + register unsigned long *cook, *raw0; + unsigned long dough[32]; + register int i; + + cook = dough; + for( i = 0; i < 16; i++, raw1++ ) { + raw0 = raw1++; + *cook = (*raw0 & 0x00fc0000L) << 6; + *cook |= (*raw0 & 0x00000fc0L) << 10; + *cook |= (*raw1 & 0x00fc0000L) >> 10; + *cook++ |= (*raw1 & 0x00000fc0L) >> 6; + *cook = (*raw0 & 0x0003f000L) << 12; + *cook |= (*raw0 & 0x0000003fL) << 16; + *cook |= (*raw1 & 0x0003f000L) >> 4; + *cook++ |= (*raw1 & 0x0000003fL); + } + usekey(dough); + return; + } + +void cpkey(into) +register unsigned long *into; +{ + register unsigned long *from, *endp; + + from = KnL, endp = &KnL[32]; + while( from < endp ) *into++ = *from++; + return; + } + +void usekey(from) +register unsigned long *from; +{ + register unsigned long *to, *endp; + + to = KnL, endp = &KnL[32]; + while( to < endp ) *to++ = *from++; + return; + } + +void des(inblock, outblock) +unsigned char *inblock, *outblock; +{ + unsigned long work[2]; + + scrunch(inblock, work); + desfunc(work, KnL); + unscrun(work, outblock); + return; + } + +static void scrunch(outof, into) +register unsigned char *outof; +register unsigned long *into; +{ + *into = (*outof++ & 0xffL) << 24; + *into |= (*outof++ & 0xffL) << 16; + *into |= (*outof++ & 0xffL) << 8; + *into++ |= (*outof++ & 0xffL); + *into = (*outof++ & 0xffL) << 24; + *into |= (*outof++ & 0xffL) << 16; + *into |= (*outof++ & 0xffL) << 8; + *into |= (*outof & 0xffL); + return; + } + +static void unscrun(outof, into) +register unsigned long *outof; +register unsigned char *into; +{ + *into++ = (unsigned char)((*outof >> 24) & 0xffL); + *into++ = (unsigned char)((*outof >> 16) & 0xffL); + *into++ = (unsigned char)((*outof >> 8) & 0xffL); + *into++ = (unsigned char)(*outof++ & 0xffL); + *into++ = (unsigned char)((*outof >> 24) & 0xffL); + *into++ = (unsigned char)((*outof >> 16) & 0xffL); + *into++ = (unsigned char)((*outof >> 8) & 0xffL); + *into = (unsigned char)(*outof & 0xffL); + return; + } + +static unsigned long SP1[64] = { + 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L, + 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L, + 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L, + 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L, + 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L, + 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L, + 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L, + 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L, + 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L, + 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L, + 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L, + 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L, + 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L, + 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L, + 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L, + 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L }; + +static unsigned long SP2[64] = { + 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L, + 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L, + 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L, + 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L, + 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L, + 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L, + 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L, + 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L, + 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L, + 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L, + 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L, + 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L, + 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L, + 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L, + 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L, + 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L }; + +static unsigned long SP3[64] = { + 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L, + 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L, + 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L, + 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L, + 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L, + 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L, + 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L, + 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L, + 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L, + 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L, + 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L, + 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L, + 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L, + 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L, + 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L, + 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L }; + +static unsigned long SP4[64] = { + 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, + 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L, + 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L, + 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L, + 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L, + 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L, + 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L, + 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L, + 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L, + 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L, + 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L, + 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, + 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L, + 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L, + 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L, + 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L }; + +static unsigned long SP5[64] = { + 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L, + 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L, + 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L, + 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L, + 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L, + 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L, + 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L, + 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L, + 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L, + 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L, + 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L, + 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L, + 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L, + 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L, + 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L, + 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L }; + +static unsigned long SP6[64] = { + 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L, + 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L, + 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L, + 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L, + 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L, + 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L, + 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L, + 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L, + 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L, + 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L, + 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L, + 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L, + 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L, + 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L, + 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L, + 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L }; + +static unsigned long SP7[64] = { + 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L, + 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L, + 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L, + 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L, + 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L, + 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L, + 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L, + 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L, + 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L, + 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L, + 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L, + 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L, + 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L, + 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L, + 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L, + 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L }; + +static unsigned long SP8[64] = { + 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L, + 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L, + 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L, + 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L, + 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L, + 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L, + 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L, + 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L, + 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L, + 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L, + 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L, + 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L, + 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L, + 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L, + 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L, + 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L }; + +static void desfunc(block, keys) +register unsigned long *block, *keys; +{ + register unsigned long fval, work, right, leftt; + register int round; + + leftt = block[0]; + right = block[1]; + work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL; + right ^= work; + leftt ^= (work << 4); + work = ((leftt >> 16) ^ right) & 0x0000ffffL; + right ^= work; + leftt ^= (work << 16); + work = ((right >> 2) ^ leftt) & 0x33333333L; + leftt ^= work; + right ^= (work << 2); + work = ((right >> 8) ^ leftt) & 0x00ff00ffL; + leftt ^= work; + right ^= (work << 8); + right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL; + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL; + + for( round = 0; round < 8; round++ ) { + work = (right << 28) | (right >> 4); + work ^= *keys++; + fval = SP7[ work & 0x3fL]; + fval |= SP5[(work >> 8) & 0x3fL]; + fval |= SP3[(work >> 16) & 0x3fL]; + fval |= SP1[(work >> 24) & 0x3fL]; + work = right ^ *keys++; + fval |= SP8[ work & 0x3fL]; + fval |= SP6[(work >> 8) & 0x3fL]; + fval |= SP4[(work >> 16) & 0x3fL]; + fval |= SP2[(work >> 24) & 0x3fL]; + leftt ^= fval; + work = (leftt << 28) | (leftt >> 4); + work ^= *keys++; + fval = SP7[ work & 0x3fL]; + fval |= SP5[(work >> 8) & 0x3fL]; + fval |= SP3[(work >> 16) & 0x3fL]; + fval |= SP1[(work >> 24) & 0x3fL]; + work = leftt ^ *keys++; + fval |= SP8[ work & 0x3fL]; + fval |= SP6[(work >> 8) & 0x3fL]; + fval |= SP4[(work >> 16) & 0x3fL]; + fval |= SP2[(work >> 24) & 0x3fL]; + right ^= fval; + } + + right = (right << 31) | (right >> 1); + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = (leftt << 31) | (leftt >> 1); + work = ((leftt >> 8) ^ right) & 0x00ff00ffL; + right ^= work; + leftt ^= (work << 8); + work = ((leftt >> 2) ^ right) & 0x33333333L; + right ^= work; + leftt ^= (work << 2); + work = ((right >> 16) ^ leftt) & 0x0000ffffL; + leftt ^= work; + right ^= (work << 16); + work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL; + leftt ^= work; + right ^= (work << 4); + *block++ = right; + *block = leftt; + return; + } + +/* Validation sets: + * + * Single-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef + * Plain : 0123 4567 89ab cde7 + * Cipher : c957 4425 6a5e d31d + * + * Double-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 + * Plain : 0123 4567 89ab cde7 + * Cipher : 7f1d 0a77 826b 8aff + * + * Double-length key, double-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 + * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff + * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7 + * + * Triple-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567 + * Plain : 0123 4567 89ab cde7 + * Cipher : de0b 7c06 ae5e 0ed5 + * + * Triple-length key, double-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567 + * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff + * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5 + * + * d3des V5.0a rwo 9208.07 18:44 Graven Imagery + **********************************************************************/ diff --git a/common/rfb/d3des.h b/common/rfb/d3des.h new file mode 100644 index 00000000..ea3da44c --- /dev/null +++ b/common/rfb/d3des.h @@ -0,0 +1,51 @@ +/* + * This is D3DES (V5.09) by Richard Outerbridge with the double and + * triple-length support removed for use in VNC. + * + * These changes are: + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * 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. + */ + +/* d3des.h - + * + * Headers and defines for d3des.c + * Graven Imagery, 1992. + * + * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge + * (GEnie : OUTER; CIS : [71755,204]) + */ + +#define EN0 0 /* MODE == encrypt */ +#define DE1 1 /* MODE == decrypt */ + +extern void deskey(unsigned char *, int); +/* hexkey[8] MODE + * Sets the internal key register according to the hexadecimal + * key contained in the 8 bytes of hexkey, according to the DES, + * for encryption or decryption according to MODE. + */ + +extern void usekey(unsigned long *); +/* cookedkey[32] + * Loads the internal key register with the data in cookedkey. + */ + +extern void cpkey(unsigned long *); +/* cookedkey[32] + * Copies the contents of the internal key register into the storage + * located at &cookedkey[0]. + */ + +extern void des(unsigned char *, unsigned char *); +/* from[8] to[8] + * Encrypts/Decrypts (according to the key currently loaded in the + * internal key register) one block of eight bytes at address 'from' + * into the block at address 'to'. They can be the same. + */ + +/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery + ********************************************************************/ diff --git a/common/rfb/encodings.cxx b/common/rfb/encodings.cxx new file mode 100644 index 00000000..6aa81c4f --- /dev/null +++ b/common/rfb/encodings.cxx @@ -0,0 +1,49 @@ +/* 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 <string.h> +#ifdef _WIN32 +#define strcasecmp _stricmp +#endif +#include <rfb/encodings.h> +#include <rfb/util.h> + +int rfb::encodingNum(const char* name) +{ + if (strcasecmp(name, "raw") == 0) return encodingRaw; + if (strcasecmp(name, "copyRect") == 0) return encodingCopyRect; + if (strcasecmp(name, "RRE") == 0) return encodingRRE; + if (strcasecmp(name, "CoRRE") == 0) return encodingCoRRE; + if (strcasecmp(name, "hextile") == 0) return encodingHextile; + if (strcasecmp(name, "ZRLE") == 0) return encodingZRLE; + if (strcasecmp(name, "Tight") == 0) return encodingTight; + return -1; +} + +const char* rfb::encodingName(unsigned int num) +{ + switch (num) { + case encodingRaw: return "raw"; + case encodingCopyRect: return "copyRect"; + case encodingRRE: return "RRE"; + case encodingCoRRE: return "CoRRE"; + case encodingHextile: return "hextile"; + case encodingZRLE: return "ZRLE"; + case encodingTight: return "Tight"; + default: return "[unknown encoding]"; + } +} diff --git a/common/rfb/encodings.h b/common/rfb/encodings.h new file mode 100644 index 00000000..51f6f1ec --- /dev/null +++ b/common/rfb/encodings.h @@ -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_ENCODINGS_H__ +#define __RFB_ENCODINGS_H__ + +namespace rfb { + + const unsigned int encodingRaw = 0; + const unsigned int encodingCopyRect = 1; + const unsigned int encodingRRE = 2; + const unsigned int encodingCoRRE = 4; + const unsigned int encodingHextile = 5; + const unsigned int encodingTight = 7; + const unsigned int encodingZRLE = 16; + + const unsigned int encodingMax = 255; + + const unsigned int pseudoEncodingXCursor = 0xffffff10; + const unsigned int pseudoEncodingCursor = 0xffffff11; + const unsigned int pseudoEncodingDesktopSize = 0xffffff21; + + // TightVNC-specific + const unsigned int pseudoEncodingLastRect = 0xFFFFFF20; + const unsigned int pseudoEncodingQualityLevel0 = 0xFFFFFFE0; + const unsigned int pseudoEncodingQualityLevel9 = 0xFFFFFFE9; + const unsigned int pseudoEncodingCompressLevel0 = 0xFFFFFF00; + const unsigned int pseudoEncodingCompressLevel9 = 0xFFFFFF09; + + int encodingNum(const char* name); + const char* encodingName(unsigned int num); +} +#endif diff --git a/common/rfb/fttypes.h b/common/rfb/fttypes.h new file mode 100644 index 00000000..4404508c --- /dev/null +++ b/common/rfb/fttypes.h @@ -0,0 +1,92 @@ +/* Copyright (C) 2005 TightVNC Team. All Rights Reserved. + * + * Developed by Dennis Syrovatsky. + * + * 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. + */ + +// -=- fttypes.h + +#ifndef __RFB_FTTYPES_H__ +#define __RFB_FTTYPES_H__ + +#include "stdio.h" +#include "stdlib.h" +#include "string.h" + +#define FT_FILENAME_SIZE 256 + +#define FT_MAX_STATUS_STRINGS 255 +#define FT_MAX_LENGTH_STATUS_STRINGS 130 + +#define FT_MAX_SENDING_SIZE 8192 + +#define FT_NET_ATTR_DIR ((unsigned int)-1) + +#define FT_ATTR_UNKNOWN 0x00000000 +#define FT_ATTR_FILE 0x00000001 +#define FT_ATTR_DIR 0x00000002 + +#define FT_ATTR_RESIZE_NEEDED 0x00040000 +#define FT_ATTR_FOLDER_EXISTS 0x00080000 +#define FT_ATTR_COPY_OVERWRITE 0x00100000 +#define FT_ATTR_FLR_UPLOAD_CHECK 0x00200000 +#define FT_ATTR_FLR_UPLOAD_ADD 0x00400000 +#define FT_ATTR_COPY_UPLOAD 0x00800000 +#define FT_ATTR_FLR_DOWNLOAD_CHECK 0x01000000 +#define FT_ATTR_FLR_DOWNLOAD_ADD 0x02000000 +#define FT_ATTR_COPY_DOWNLOAD 0x04000000 +#define FT_ATTR_DELETE_LOCAL 0x08000000 +#define FT_ATTR_DELETE_REMOTE 0x10000000 +#define FT_ATTR_RENAME_LOCAL 0x20000000 +#define FT_ATTR_RENAME_REMOTE 0x40000000 + +#define FT_FLR_DEST_MAIN 101 +#define FT_FLR_DEST_BROWSE 102 +#define FT_FLR_DEST_DOWNLOAD 103 +#define FT_FLR_DEST_UPLOAD 104 +#define FT_FLR_DEST_DELETE 105 +#define FT_FLR_DEST_RENAME 106 + +typedef struct tagSIZEDATAINFO +{ + unsigned int size; + unsigned int data; +} SIZEDATAINFO; + +typedef struct tagSIZEDATAFLAGSINFO +{ + unsigned int size; + unsigned int data; + unsigned int flags; +} SIZEDATAFLAGSINFO; + +typedef struct tagFILEINFO +{ + char name[FT_FILENAME_SIZE]; + SIZEDATAFLAGSINFO info; +} FILEINFO; + +typedef struct tagFILEINFOEX +{ + char locPath[FT_FILENAME_SIZE]; + char locName[FT_FILENAME_SIZE]; + char remPath[FT_FILENAME_SIZE]; + char remName[FT_FILENAME_SIZE]; + SIZEDATAFLAGSINFO info; +} FILEINFOEX; + +#endif // __RFB_FTTYPES_H__ diff --git a/common/rfb/hextileConstants.h b/common/rfb/hextileConstants.h new file mode 100644 index 00000000..b8713cb6 --- /dev/null +++ b/common/rfb/hextileConstants.h @@ -0,0 +1,27 @@ +/* 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_HEXTILECONSTANTS_H__ +#define __RFB_HEXTILECONSTANTS_H__ +namespace rfb { + const int hextileRaw = (1 << 0); + const int hextileBgSpecified = (1 << 1); + const int hextileFgSpecified = (1 << 2); + const int hextileAnySubrects = (1 << 3); + const int hextileSubrectsColoured = (1 << 4); +} +#endif diff --git a/common/rfb/hextileDecode.h b/common/rfb/hextileDecode.h new file mode 100644 index 00000000..77befc7d --- /dev/null +++ b/common/rfb/hextileDecode.h @@ -0,0 +1,126 @@ +/* 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. + */ +// +// Hextile decoding function. +// +// This file is #included after having set the following macros: +// BPP - 8, 16 or 32 +// EXTRA_ARGS - optional extra arguments +// FILL_RECT - fill a rectangle with a single colour +// IMAGE_RECT - draw a rectangle of pixel data from a buffer + +#include <rdr/InStream.h> +#include <rfb/hextileConstants.h> + +namespace rfb { + +// CONCAT2E concatenates its arguments, expanding them if they are macros + +#ifndef CONCAT2E +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) +#endif + +#define PIXEL_T rdr::CONCAT2E(U,BPP) +#define READ_PIXEL CONCAT2E(readOpaque,BPP) +#define HEXTILE_DECODE CONCAT2E(hextileDecode,BPP) + +void HEXTILE_DECODE (const Rect& r, rdr::InStream* is, PIXEL_T* buf +#ifdef EXTRA_ARGS + , EXTRA_ARGS +#endif + ) +{ + Rect t; + PIXEL_T bg = 0; + PIXEL_T fg = 0; + + 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) { + + t.br.x = __rfbmin(r.br.x, t.tl.x + 16); + + int tileType = is->readU8(); + + if (tileType & hextileRaw) { + is->readBytes(buf, t.area() * (BPP/8)); + IMAGE_RECT(t, buf); + continue; + } + + if (tileType & hextileBgSpecified) + bg = is->READ_PIXEL(); + +#ifdef FAVOUR_FILL_RECT + FILL_RECT(t, bg); +#else + int len = t.area(); + PIXEL_T* ptr = (PIXEL_T*)buf; + while (len-- > 0) *ptr++ = bg; +#endif + + if (tileType & hextileFgSpecified) + fg = is->READ_PIXEL(); + + if (tileType & hextileAnySubrects) { + int nSubrects = is->readU8(); + + for (int i = 0; i < nSubrects; i++) { + + if (tileType & hextileSubrectsColoured) + fg = is->READ_PIXEL(); + + int xy = is->readU8(); + int wh = is->readU8(); + +#ifdef FAVOUR_FILL_RECT + Rect s; + s.tl.x = t.tl.x + ((xy >> 4) & 15); + s.tl.y = t.tl.y + (xy & 15); + s.br.x = s.tl.x + ((wh >> 4) & 15) + 1; + s.br.y = s.tl.y + (wh & 15) + 1; + FILL_RECT(s, fg); +#else + int x = ((xy >> 4) & 15); + int y = (xy & 15); + int w = ((wh >> 4) & 15) + 1; + int h = (wh & 15) + 1; + PIXEL_T* ptr = (PIXEL_T*)buf + y * t.width() + x; + int rowAdd = t.width() - w; + while (h-- > 0) { + int len = w; + while (len-- > 0) *ptr++ = fg; + ptr += rowAdd; + } +#endif + } + } +#ifndef FAVOUR_FILL_RECT + IMAGE_RECT(t, buf); +#endif + } + } +} + +#undef PIXEL_T +#undef READ_PIXEL +#undef HEXTILE_DECODE +} diff --git a/common/rfb/hextileEncode.h b/common/rfb/hextileEncode.h new file mode 100644 index 00000000..9f8bd558 --- /dev/null +++ b/common/rfb/hextileEncode.h @@ -0,0 +1,229 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2005 Constantin Kaplinsky. 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. + */ +// +// Hextile encoding function. +// +// This file is #included after having set the following macros: +// BPP - 8, 16 or 32 +// EXTRA_ARGS - optional extra arguments +// GET_IMAGE_INTO_BUF - gets a rectangle of pixel data into a buffer + +#include <rdr/OutStream.h> +#include <rfb/hextileConstants.h> + +namespace rfb { + +// CONCAT2E concatenates its arguments, expanding them if they are macros + +#ifndef CONCAT2E +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) +#endif + +#define PIXEL_T rdr::CONCAT2E(U,BPP) +#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP) +#define HEXTILE_ENCODE CONCAT2E(hextileEncode,BPP) +#define HEXTILE_ENCODE_TILE CONCAT2E(hextileEncodeTile,BPP) +#define TEST_TILE_TYPE CONCAT2E(hextileTestTileType,BPP) + +int TEST_TILE_TYPE (PIXEL_T* data, int w, int h, PIXEL_T* bg, PIXEL_T* fg); +int HEXTILE_ENCODE_TILE (PIXEL_T* data, int w, int h, int tileType, + rdr::U8* encoded, PIXEL_T bg); + +void HEXTILE_ENCODE(const Rect& r, rdr::OutStream* os +#ifdef EXTRA_ARGS + , EXTRA_ARGS +#endif + ) +{ + Rect t; + PIXEL_T buf[256]; + PIXEL_T oldBg = 0, oldFg = 0; + bool oldBgValid = false; + bool oldFgValid = false; + rdr::U8 encoded[256*(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) { + + t.br.x = __rfbmin(r.br.x, t.tl.x + 16); + + GET_IMAGE_INTO_BUF(t,buf); + + PIXEL_T bg, fg; + int tileType = TEST_TILE_TYPE(buf, t.width(), t.height(), &bg, &fg); + + if (!oldBgValid || oldBg != bg) { + tileType |= hextileBgSpecified; + oldBg = bg; + oldBgValid = true; + } + + int encodedLen = 0; + + if (tileType & hextileAnySubrects) { + + if (tileType & hextileSubrectsColoured) { + oldFgValid = false; + } else { + if (!oldFgValid || oldFg != fg) { + tileType |= hextileFgSpecified; + oldFg = fg; + oldFgValid = true; + } + } + + encodedLen = HEXTILE_ENCODE_TILE(buf, t.width(), t.height(), tileType, + encoded, bg); + + if (encodedLen < 0) { + GET_IMAGE_INTO_BUF(t,buf); + os->writeU8(hextileRaw); + os->writeBytes(buf, t.width() * t.height() * (BPP/8)); + oldBgValid = oldFgValid = false; + continue; + } + } + + os->writeU8(tileType); + if (tileType & hextileBgSpecified) os->WRITE_PIXEL(bg); + if (tileType & hextileFgSpecified) os->WRITE_PIXEL(fg); + if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen); + } + } +} + + +int HEXTILE_ENCODE_TILE (PIXEL_T* data, int w, int h, int tileType, + rdr::U8* encoded, PIXEL_T bg) +{ + rdr::U8* nSubrectsPtr = encoded; + *nSubrectsPtr = 0; + encoded++; + + for (int y = 0; y < h; y++) + { + int x = 0; + while (x < w) { + if (*data == bg) { + x++; + data++; + continue; + } + + // Find horizontal subrect first + PIXEL_T* ptr = data+1; + PIXEL_T* eol = data+w-x; + while (ptr < eol && *ptr == *data) ptr++; + int sw = ptr - data; + + ptr = data + w; + int sh = 1; + while (sh < h-y) { + eol = ptr + sw; + while (ptr < eol) + if (*ptr++ != *data) goto endOfSubrect; + ptr += w - sw; + sh++; + } + endOfSubrect: + + (*nSubrectsPtr)++; + + if (tileType & hextileSubrectsColoured) { + if (encoded - nSubrectsPtr + (BPP/8) > w*h*(BPP/8)) return -1; +#if (BPP == 8) + *encoded++ = *data; +#elif (BPP == 16) + *encoded++ = ((rdr::U8*)data)[0]; + *encoded++ = ((rdr::U8*)data)[1]; +#elif (BPP == 32) + *encoded++ = ((rdr::U8*)data)[0]; + *encoded++ = ((rdr::U8*)data)[1]; + *encoded++ = ((rdr::U8*)data)[2]; + *encoded++ = ((rdr::U8*)data)[3]; +#endif + } + + if (encoded - nSubrectsPtr + 2 > w*h*(BPP/8)) return -1; + *encoded++ = (x << 4) | y; + *encoded++ = ((sw-1) << 4) | (sh-1); + + ptr = data+w; + PIXEL_T* eor = data+w*sh; + while (ptr < eor) { + eol = ptr + sw; + while (ptr < eol) *ptr++ = bg; + ptr += w - sw; + } + x += sw; + data += sw; + } + } + return encoded - nSubrectsPtr; +} + + +int TEST_TILE_TYPE (PIXEL_T* data, int w, int h, PIXEL_T* bg, PIXEL_T* fg) +{ + PIXEL_T pix1 = *data; + PIXEL_T* end = data + w * h; + + PIXEL_T* ptr = data + 1; + while (ptr < end && *ptr == pix1) + ptr++; + + if (ptr == end) { + *bg = pix1; + return 0; // solid-color tile + } + + int count1 = ptr - data; + int count2 = 1; + PIXEL_T pix2 = *ptr++; + int tileType = hextileAnySubrects; + + for (; ptr < end; ptr++) { + if (*ptr == pix1) { + count1++; + } else if (*ptr == pix2) { + count2++; + } else { + tileType |= hextileSubrectsColoured; + break; + } + } + + if (count1 >= count2) { + *bg = pix1; *fg = pix2; + } else { + *bg = pix2; *fg = pix1; + } + return tileType; +} + +#undef PIXEL_T +#undef WRITE_PIXEL +#undef HEXTILE_ENCODE +#undef HEXTILE_ENCODE_TILE +#undef TEST_TILE_TYPE +} diff --git a/common/rfb/hextileEncodeBetter.h b/common/rfb/hextileEncodeBetter.h new file mode 100644 index 00000000..2b6b160a --- /dev/null +++ b/common/rfb/hextileEncodeBetter.h @@ -0,0 +1,352 @@ +/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2005 Constantin Kaplinsky. 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. + */ +// +// Hextile encoding function. +// +// This file is #included after having set the following macros: +// BPP - 8, 16 or 32 +// EXTRA_ARGS - optional extra arguments +// GET_IMAGE_INTO_BUF - gets a rectangle of pixel data into a buffer + +#include <rdr/OutStream.h> +#include <rfb/hextileConstants.h> +#include <rfb/TightPalette.h> + +#include <assert.h> + +namespace rfb { + +// CONCAT2E concatenates its arguments, expanding them if they are macros + +#ifndef CONCAT2E +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) +#endif + +#define PIXEL_T rdr::CONCAT2E(U,BPP) +#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP) +#define HEXTILE_TILE CONCAT2E(HextileTile,BPP) +#define HEXTILE_ENCODE CONCAT2E(hextileEncodeBetter,BPP) + +// +// This class analyzes a separate tile and encodes its subrectangles. +// + +class HEXTILE_TILE { + + public: + + HEXTILE_TILE (); + + // + // Initialize existing object instance with new tile data. + // + void newTile(const PIXEL_T *src, int w, int h); + + // + // Flags can include: hextileRaw, hextileAnySubrects and + // hextileSubrectsColoured. Note that if hextileRaw is set, other + // flags make no sense. Also, hextileSubrectsColoured is meaningful + // only when hextileAnySubrects is set as well. + // + int getFlags() const { return m_flags; } + + // + // Returns the size of encoded subrects data, including subrect count. + // The size is zero if flags do not include hextileAnySubrects. + // + int getSize() const { return m_size; } + + // + // Return optimal background. + // + int getBackground() const { return m_background; } + + // + // Return foreground if flags include hextileSubrectsColoured. + // + int getForeground() const { return m_foreground; } + + // + // Encode subrects. This function may be called only if + // hextileAnySubrects bit is set in flags. The buffer size should be + // big enough to store at least the number of bytes returned by the + // getSize() method. + // + void encode(rdr::U8* dst) const; + + protected: + + // + // Analyze the tile pixels, fill in all the data fields. + // + void analyze(); + + const PIXEL_T *m_tile; + int m_width; + int m_height; + + int m_size; + int m_flags; + PIXEL_T m_background; + PIXEL_T m_foreground; + + int m_numSubrects; + rdr::U8 m_coords[256 * 2]; + PIXEL_T m_colors[256]; + + private: + + bool m_processed[16][16]; + TightPalette m_pal; +}; + +HEXTILE_TILE::HEXTILE_TILE() + : m_tile(NULL), m_width(0), m_height(0), + m_size(0), m_flags(0), m_background(0), m_foreground(0), + m_numSubrects(0), m_pal(48 + 2 * BPP) +{ +} + +void HEXTILE_TILE::newTile(const PIXEL_T *src, int w, int h) +{ + m_tile = src; + m_width = w; + m_height = h; + + analyze(); +} + +void HEXTILE_TILE::analyze() +{ + assert(m_tile && m_width && m_height); + + const PIXEL_T *ptr = m_tile; + const PIXEL_T *end = &m_tile[m_width * m_height]; + PIXEL_T color = *ptr++; + while (ptr != end && *ptr == color) + ptr++; + + // Handle solid tile + if (ptr == end) { + m_background = m_tile[0]; + m_flags = 0; + m_size = 0; + return; + } + + // Compute number of complete rows of the same color, at the top + int y = (ptr - m_tile) / m_width; + + PIXEL_T *colorsPtr = m_colors; + rdr::U8 *coordsPtr = m_coords; + m_pal.reset(); + m_numSubrects = 0; + + // Have we found the first subrect already? + if (y > 0) { + *colorsPtr++ = color; + *coordsPtr++ = 0; + *coordsPtr++ = (rdr::U8)(((m_width - 1) << 4) | ((y - 1) & 0x0F)); + m_pal.insert(color, 1); + m_numSubrects++; + } + + memset(m_processed, 0, 16 * 16 * sizeof(bool)); + + int x, sx, sy, sw, sh, max_x; + + for (; y < m_height; y++) { + for (x = 0; x < m_width; x++) { + // Skip pixels that were processed earlier + if (m_processed[y][x]) { + continue; + } + // Determine dimensions of the horizontal subrect + color = m_tile[y * m_width + x]; + for (sx = x + 1; sx < m_width; sx++) { + if (m_tile[y * m_width + sx] != color) + break; + } + sw = sx - x; + max_x = sx; + for (sy = y + 1; sy < m_height; sy++) { + for (sx = x; sx < max_x; sx++) { + if (m_tile[sy * m_width + sx] != color) + goto done; + } + } + done: + sh = sy - y; + + // Save properties of this subrect + *colorsPtr++ = color; + *coordsPtr++ = (rdr::U8)((x << 4) | (y & 0x0F)); + *coordsPtr++ = (rdr::U8)(((sw - 1) << 4) | ((sh - 1) & 0x0F)); + + if (m_pal.insert(color, 1) == 0) { + // Handle palette overflow + m_flags = hextileRaw; + m_size = 0; + return; + } + + m_numSubrects++; + + // Mark pixels of this subrect as processed, below this row + for (sy = y + 1; sy < y + sh; sy++) { + for (sx = x; sx < x + sw; sx++) + m_processed[sy][sx] = true; + } + + // Skip processed pixels of this row + x += (sw - 1); + } + } + + // Save number of colors in this tile (should be no less than 2) + int numColors = m_pal.getNumColors(); + assert(numColors >= 2); + + m_background = (PIXEL_T)m_pal.getEntry(0); + m_flags = hextileAnySubrects; + int numSubrects = m_numSubrects - m_pal.getCount(0); + + if (numColors == 2) { + // Monochrome tile + m_foreground = (PIXEL_T)m_pal.getEntry(1); + m_size = 1 + 2 * numSubrects; + } else { + // Colored tile + m_flags |= hextileSubrectsColoured; + m_size = 1 + (2 + (BPP/8)) * numSubrects; + } +} + +void HEXTILE_TILE::encode(rdr::U8 *dst) const +{ + assert(m_numSubrects && (m_flags & hextileAnySubrects)); + + // Zero subrects counter + rdr::U8 *numSubrectsPtr = dst; + *dst++ = 0; + + for (int i = 0; i < m_numSubrects; i++) { + if (m_colors[i] == m_background) + continue; + + if (m_flags & hextileSubrectsColoured) { +#if (BPP == 8) + *dst++ = m_colors[i]; +#elif (BPP == 16) + *dst++ = ((rdr::U8*)&m_colors[i])[0]; + *dst++ = ((rdr::U8*)&m_colors[i])[1]; +#elif (BPP == 32) + *dst++ = ((rdr::U8*)&m_colors[i])[0]; + *dst++ = ((rdr::U8*)&m_colors[i])[1]; + *dst++ = ((rdr::U8*)&m_colors[i])[2]; + *dst++ = ((rdr::U8*)&m_colors[i])[3]; +#endif + } + *dst++ = m_coords[i * 2]; + *dst++ = m_coords[i * 2 + 1]; + + (*numSubrectsPtr)++; + } + + assert(dst - numSubrectsPtr == m_size); +} + +// +// Main encoding function. +// + +void HEXTILE_ENCODE(const Rect& r, rdr::OutStream* os +#ifdef EXTRA_ARGS + , EXTRA_ARGS +#endif + ) +{ + Rect t; + PIXEL_T buf[256]; + PIXEL_T oldBg = 0, oldFg = 0; + bool oldBgValid = false; + bool oldFgValid = false; + rdr::U8 encoded[256*(BPP/8)]; + + HEXTILE_TILE tile; + + 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) { + + t.br.x = __rfbmin(r.br.x, t.tl.x + 16); + + GET_IMAGE_INTO_BUF(t,buf); + + tile.newTile(buf, t.width(), t.height()); + int tileType = tile.getFlags(); + int encodedLen = tile.getSize(); + + if ( (tileType & hextileRaw) != 0 || + encodedLen >= t.width() * t.height() * (BPP/8)) { + os->writeU8(hextileRaw); + os->writeBytes(buf, t.width() * t.height() * (BPP/8)); + oldBgValid = oldFgValid = false; + continue; + } + + PIXEL_T bg = tile.getBackground(); + PIXEL_T fg = 0; + + if (!oldBgValid || oldBg != bg) { + tileType |= hextileBgSpecified; + oldBg = bg; + oldBgValid = true; + } + + if (tileType & hextileAnySubrects) { + if (tileType & hextileSubrectsColoured) { + oldFgValid = false; + } else { + fg = tile.getForeground(); + if (!oldFgValid || oldFg != fg) { + tileType |= hextileFgSpecified; + oldFg = fg; + oldFgValid = true; + } + } + tile.encode(encoded); + } + + os->writeU8(tileType); + if (tileType & hextileBgSpecified) os->WRITE_PIXEL(bg); + if (tileType & hextileFgSpecified) os->WRITE_PIXEL(fg); + if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen); + } + } +} + +#undef PIXEL_T +#undef WRITE_PIXEL +#undef HEXTILE_TILE +#undef HEXTILE_ENCODE +} diff --git a/common/rfb/keysymdef.h b/common/rfb/keysymdef.h new file mode 100644 index 00000000..979ebdd5 --- /dev/null +++ b/common/rfb/keysymdef.h @@ -0,0 +1,1595 @@ +/* $TOG: keysymdef.h /main/28 1998/05/22 16:18:01 kaleb $ */ + +/*********************************************************** +Copyright 1987, 1994, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +#define XK_VoidSymbol 0xFFFFFF /* void symbol */ + +#ifdef XK_MISCELLANY +/* + * TTY Functions, cleverly chosen to map to ascii, for convenience of + * programming, but could have been arbitrary (at the cost of lookup + * tables in client code. + */ + +#define XK_BackSpace 0xFF08 /* back space, back char */ +#define XK_Tab 0xFF09 +#define XK_Linefeed 0xFF0A /* Linefeed, LF */ +#define XK_Clear 0xFF0B +#define XK_Return 0xFF0D /* Return, enter */ +#define XK_Pause 0xFF13 /* Pause, hold */ +#define XK_Scroll_Lock 0xFF14 +#define XK_Sys_Req 0xFF15 +#define XK_Escape 0xFF1B +#define XK_Delete 0xFFFF /* Delete, rubout */ + + + +/* International & multi-key character composition */ + +#define XK_Multi_key 0xFF20 /* Multi-key character compose */ +#define XK_Codeinput 0xFF37 +#define XK_SingleCandidate 0xFF3C +#define XK_MultipleCandidate 0xFF3D +#define XK_PreviousCandidate 0xFF3E + +/* Japanese keyboard support */ + +#define XK_Kanji 0xFF21 /* Kanji, Kanji convert */ +#define XK_Muhenkan 0xFF22 /* Cancel Conversion */ +#define XK_Henkan_Mode 0xFF23 /* Start/Stop Conversion */ +#define XK_Henkan 0xFF23 /* Alias for Henkan_Mode */ +#define XK_Romaji 0xFF24 /* to Romaji */ +#define XK_Hiragana 0xFF25 /* to Hiragana */ +#define XK_Katakana 0xFF26 /* to Katakana */ +#define XK_Hiragana_Katakana 0xFF27 /* Hiragana/Katakana toggle */ +#define XK_Zenkaku 0xFF28 /* to Zenkaku */ +#define XK_Hankaku 0xFF29 /* to Hankaku */ +#define XK_Zenkaku_Hankaku 0xFF2A /* Zenkaku/Hankaku toggle */ +#define XK_Touroku 0xFF2B /* Add to Dictionary */ +#define XK_Massyo 0xFF2C /* Delete from Dictionary */ +#define XK_Kana_Lock 0xFF2D /* Kana Lock */ +#define XK_Kana_Shift 0xFF2E /* Kana Shift */ +#define XK_Eisu_Shift 0xFF2F /* Alphanumeric Shift */ +#define XK_Eisu_toggle 0xFF30 /* Alphanumeric toggle */ +#define XK_Kanji_Bangou 0xFF37 /* Codeinput */ +#define XK_Zen_Koho 0xFF3D /* Multiple/All Candidate(s) */ +#define XK_Mae_Koho 0xFF3E /* Previous Candidate */ + +/* 0xFF31 thru 0xFF3F are under XK_KOREAN */ + +/* Cursor control & motion */ + +#define XK_Home 0xFF50 +#define XK_Left 0xFF51 /* Move left, left arrow */ +#define XK_Up 0xFF52 /* Move up, up arrow */ +#define XK_Right 0xFF53 /* Move right, right arrow */ +#define XK_Down 0xFF54 /* Move down, down arrow */ +#define XK_Prior 0xFF55 /* Prior, previous */ +#define XK_Page_Up 0xFF55 +#define XK_Next 0xFF56 /* Next */ +#define XK_Page_Down 0xFF56 +#define XK_End 0xFF57 /* EOL */ +#define XK_Begin 0xFF58 /* BOL */ + + +/* Misc Functions */ + +#define XK_Select 0xFF60 /* Select, mark */ +#define XK_Print 0xFF61 +#define XK_Execute 0xFF62 /* Execute, run, do */ +#define XK_Insert 0xFF63 /* Insert, insert here */ +#define XK_Undo 0xFF65 /* Undo, oops */ +#define XK_Redo 0xFF66 /* redo, again */ +#define XK_Menu 0xFF67 +#define XK_Find 0xFF68 /* Find, search */ +#define XK_Cancel 0xFF69 /* Cancel, stop, abort, exit */ +#define XK_Help 0xFF6A /* Help */ +#define XK_Break 0xFF6B +#define XK_Mode_switch 0xFF7E /* Character set switch */ +#define XK_script_switch 0xFF7E /* Alias for mode_switch */ +#define XK_Num_Lock 0xFF7F + +/* Keypad Functions, keypad numbers cleverly chosen to map to ascii */ + +#define XK_KP_Space 0xFF80 /* space */ +#define XK_KP_Tab 0xFF89 +#define XK_KP_Enter 0xFF8D /* enter */ +#define XK_KP_F1 0xFF91 /* PF1, KP_A, ... */ +#define XK_KP_F2 0xFF92 +#define XK_KP_F3 0xFF93 +#define XK_KP_F4 0xFF94 +#define XK_KP_Home 0xFF95 +#define XK_KP_Left 0xFF96 +#define XK_KP_Up 0xFF97 +#define XK_KP_Right 0xFF98 +#define XK_KP_Down 0xFF99 +#define XK_KP_Prior 0xFF9A +#define XK_KP_Page_Up 0xFF9A +#define XK_KP_Next 0xFF9B +#define XK_KP_Page_Down 0xFF9B +#define XK_KP_End 0xFF9C +#define XK_KP_Begin 0xFF9D +#define XK_KP_Insert 0xFF9E +#define XK_KP_Delete 0xFF9F +#define XK_KP_Equal 0xFFBD /* equals */ +#define XK_KP_Multiply 0xFFAA +#define XK_KP_Add 0xFFAB +#define XK_KP_Separator 0xFFAC /* separator, often comma */ +#define XK_KP_Subtract 0xFFAD +#define XK_KP_Decimal 0xFFAE +#define XK_KP_Divide 0xFFAF + +#define XK_KP_0 0xFFB0 +#define XK_KP_1 0xFFB1 +#define XK_KP_2 0xFFB2 +#define XK_KP_3 0xFFB3 +#define XK_KP_4 0xFFB4 +#define XK_KP_5 0xFFB5 +#define XK_KP_6 0xFFB6 +#define XK_KP_7 0xFFB7 +#define XK_KP_8 0xFFB8 +#define XK_KP_9 0xFFB9 + + + +/* + * Auxilliary Functions; note the duplicate definitions for left and right + * function keys; Sun keyboards and a few other manufactures have such + * function key groups on the left and/or right sides of the keyboard. + * We've not found a keyboard with more than 35 function keys total. + */ + +#define XK_F1 0xFFBE +#define XK_F2 0xFFBF +#define XK_F3 0xFFC0 +#define XK_F4 0xFFC1 +#define XK_F5 0xFFC2 +#define XK_F6 0xFFC3 +#define XK_F7 0xFFC4 +#define XK_F8 0xFFC5 +#define XK_F9 0xFFC6 +#define XK_F10 0xFFC7 +#define XK_F11 0xFFC8 +#define XK_L1 0xFFC8 +#define XK_F12 0xFFC9 +#define XK_L2 0xFFC9 +#define XK_F13 0xFFCA +#define XK_L3 0xFFCA +#define XK_F14 0xFFCB +#define XK_L4 0xFFCB +#define XK_F15 0xFFCC +#define XK_L5 0xFFCC +#define XK_F16 0xFFCD +#define XK_L6 0xFFCD +#define XK_F17 0xFFCE +#define XK_L7 0xFFCE +#define XK_F18 0xFFCF +#define XK_L8 0xFFCF +#define XK_F19 0xFFD0 +#define XK_L9 0xFFD0 +#define XK_F20 0xFFD1 +#define XK_L10 0xFFD1 +#define XK_F21 0xFFD2 +#define XK_R1 0xFFD2 +#define XK_F22 0xFFD3 +#define XK_R2 0xFFD3 +#define XK_F23 0xFFD4 +#define XK_R3 0xFFD4 +#define XK_F24 0xFFD5 +#define XK_R4 0xFFD5 +#define XK_F25 0xFFD6 +#define XK_R5 0xFFD6 +#define XK_F26 0xFFD7 +#define XK_R6 0xFFD7 +#define XK_F27 0xFFD8 +#define XK_R7 0xFFD8 +#define XK_F28 0xFFD9 +#define XK_R8 0xFFD9 +#define XK_F29 0xFFDA +#define XK_R9 0xFFDA +#define XK_F30 0xFFDB +#define XK_R10 0xFFDB +#define XK_F31 0xFFDC +#define XK_R11 0xFFDC +#define XK_F32 0xFFDD +#define XK_R12 0xFFDD +#define XK_F33 0xFFDE +#define XK_R13 0xFFDE +#define XK_F34 0xFFDF +#define XK_R14 0xFFDF +#define XK_F35 0xFFE0 +#define XK_R15 0xFFE0 + +/* Modifiers */ + +#define XK_Shift_L 0xFFE1 /* Left shift */ +#define XK_Shift_R 0xFFE2 /* Right shift */ +#define XK_Control_L 0xFFE3 /* Left control */ +#define XK_Control_R 0xFFE4 /* Right control */ +#define XK_Caps_Lock 0xFFE5 /* Caps lock */ +#define XK_Shift_Lock 0xFFE6 /* Shift lock */ + +#define XK_Meta_L 0xFFE7 /* Left meta */ +#define XK_Meta_R 0xFFE8 /* Right meta */ +#define XK_Alt_L 0xFFE9 /* Left alt */ +#define XK_Alt_R 0xFFEA /* Right alt */ +#define XK_Super_L 0xFFEB /* Left super */ +#define XK_Super_R 0xFFEC /* Right super */ +#define XK_Hyper_L 0xFFED /* Left hyper */ +#define XK_Hyper_R 0xFFEE /* Right hyper */ +#endif /* XK_MISCELLANY */ + +/* + * ISO 9995 Function and Modifier Keys + * Byte 3 = 0xFE + */ + +#ifdef XK_XKB_KEYS +#define XK_ISO_Lock 0xFE01 +#define XK_ISO_Level2_Latch 0xFE02 +#define XK_ISO_Level3_Shift 0xFE03 +#define XK_ISO_Level3_Latch 0xFE04 +#define XK_ISO_Level3_Lock 0xFE05 +#define XK_ISO_Group_Shift 0xFF7E /* Alias for mode_switch */ +#define XK_ISO_Group_Latch 0xFE06 +#define XK_ISO_Group_Lock 0xFE07 +#define XK_ISO_Next_Group 0xFE08 +#define XK_ISO_Next_Group_Lock 0xFE09 +#define XK_ISO_Prev_Group 0xFE0A +#define XK_ISO_Prev_Group_Lock 0xFE0B +#define XK_ISO_First_Group 0xFE0C +#define XK_ISO_First_Group_Lock 0xFE0D +#define XK_ISO_Last_Group 0xFE0E +#define XK_ISO_Last_Group_Lock 0xFE0F + +#define XK_ISO_Left_Tab 0xFE20 +#define XK_ISO_Move_Line_Up 0xFE21 +#define XK_ISO_Move_Line_Down 0xFE22 +#define XK_ISO_Partial_Line_Up 0xFE23 +#define XK_ISO_Partial_Line_Down 0xFE24 +#define XK_ISO_Partial_Space_Left 0xFE25 +#define XK_ISO_Partial_Space_Right 0xFE26 +#define XK_ISO_Set_Margin_Left 0xFE27 +#define XK_ISO_Set_Margin_Right 0xFE28 +#define XK_ISO_Release_Margin_Left 0xFE29 +#define XK_ISO_Release_Margin_Right 0xFE2A +#define XK_ISO_Release_Both_Margins 0xFE2B +#define XK_ISO_Fast_Cursor_Left 0xFE2C +#define XK_ISO_Fast_Cursor_Right 0xFE2D +#define XK_ISO_Fast_Cursor_Up 0xFE2E +#define XK_ISO_Fast_Cursor_Down 0xFE2F +#define XK_ISO_Continuous_Underline 0xFE30 +#define XK_ISO_Discontinuous_Underline 0xFE31 +#define XK_ISO_Emphasize 0xFE32 +#define XK_ISO_Center_Object 0xFE33 +#define XK_ISO_Enter 0xFE34 + +#define XK_dead_grave 0xFE50 +#define XK_dead_acute 0xFE51 +#define XK_dead_circumflex 0xFE52 +#define XK_dead_tilde 0xFE53 +#define XK_dead_macron 0xFE54 +#define XK_dead_breve 0xFE55 +#define XK_dead_abovedot 0xFE56 +#define XK_dead_diaeresis 0xFE57 +#define XK_dead_abovering 0xFE58 +#define XK_dead_doubleacute 0xFE59 +#define XK_dead_caron 0xFE5A +#define XK_dead_cedilla 0xFE5B +#define XK_dead_ogonek 0xFE5C +#define XK_dead_iota 0xFE5D +#define XK_dead_voiced_sound 0xFE5E +#define XK_dead_semivoiced_sound 0xFE5F +#define XK_dead_belowdot 0xFE60 + +#define XK_First_Virtual_Screen 0xFED0 +#define XK_Prev_Virtual_Screen 0xFED1 +#define XK_Next_Virtual_Screen 0xFED2 +#define XK_Last_Virtual_Screen 0xFED4 +#define XK_Terminate_Server 0xFED5 + +#define XK_AccessX_Enable 0xFE70 +#define XK_AccessX_Feedback_Enable 0xFE71 +#define XK_RepeatKeys_Enable 0xFE72 +#define XK_SlowKeys_Enable 0xFE73 +#define XK_BounceKeys_Enable 0xFE74 +#define XK_StickyKeys_Enable 0xFE75 +#define XK_MouseKeys_Enable 0xFE76 +#define XK_MouseKeys_Accel_Enable 0xFE77 +#define XK_Overlay1_Enable 0xFE78 +#define XK_Overlay2_Enable 0xFE79 +#define XK_AudibleBell_Enable 0xFE7A + +#define XK_Pointer_Left 0xFEE0 +#define XK_Pointer_Right 0xFEE1 +#define XK_Pointer_Up 0xFEE2 +#define XK_Pointer_Down 0xFEE3 +#define XK_Pointer_UpLeft 0xFEE4 +#define XK_Pointer_UpRight 0xFEE5 +#define XK_Pointer_DownLeft 0xFEE6 +#define XK_Pointer_DownRight 0xFEE7 +#define XK_Pointer_Button_Dflt 0xFEE8 +#define XK_Pointer_Button1 0xFEE9 +#define XK_Pointer_Button2 0xFEEA +#define XK_Pointer_Button3 0xFEEB +#define XK_Pointer_Button4 0xFEEC +#define XK_Pointer_Button5 0xFEED +#define XK_Pointer_DblClick_Dflt 0xFEEE +#define XK_Pointer_DblClick1 0xFEEF +#define XK_Pointer_DblClick2 0xFEF0 +#define XK_Pointer_DblClick3 0xFEF1 +#define XK_Pointer_DblClick4 0xFEF2 +#define XK_Pointer_DblClick5 0xFEF3 +#define XK_Pointer_Drag_Dflt 0xFEF4 +#define XK_Pointer_Drag1 0xFEF5 +#define XK_Pointer_Drag2 0xFEF6 +#define XK_Pointer_Drag3 0xFEF7 +#define XK_Pointer_Drag4 0xFEF8 +#define XK_Pointer_Drag5 0xFEFD + +#define XK_Pointer_EnableKeys 0xFEF9 +#define XK_Pointer_Accelerate 0xFEFA +#define XK_Pointer_DfltBtnNext 0xFEFB +#define XK_Pointer_DfltBtnPrev 0xFEFC + +#endif + +/* + * 3270 Terminal Keys + * Byte 3 = 0xFD + */ + +#ifdef XK_3270 +#define XK_3270_Duplicate 0xFD01 +#define XK_3270_FieldMark 0xFD02 +#define XK_3270_Right2 0xFD03 +#define XK_3270_Left2 0xFD04 +#define XK_3270_BackTab 0xFD05 +#define XK_3270_EraseEOF 0xFD06 +#define XK_3270_EraseInput 0xFD07 +#define XK_3270_Reset 0xFD08 +#define XK_3270_Quit 0xFD09 +#define XK_3270_PA1 0xFD0A +#define XK_3270_PA2 0xFD0B +#define XK_3270_PA3 0xFD0C +#define XK_3270_Test 0xFD0D +#define XK_3270_Attn 0xFD0E +#define XK_3270_CursorBlink 0xFD0F +#define XK_3270_AltCursor 0xFD10 +#define XK_3270_KeyClick 0xFD11 +#define XK_3270_Jump 0xFD12 +#define XK_3270_Ident 0xFD13 +#define XK_3270_Rule 0xFD14 +#define XK_3270_Copy 0xFD15 +#define XK_3270_Play 0xFD16 +#define XK_3270_Setup 0xFD17 +#define XK_3270_Record 0xFD18 +#define XK_3270_ChangeScreen 0xFD19 +#define XK_3270_DeleteWord 0xFD1A +#define XK_3270_ExSelect 0xFD1B +#define XK_3270_CursorSelect 0xFD1C +#define XK_3270_PrintScreen 0xFD1D +#define XK_3270_Enter 0xFD1E +#endif + +/* + * Latin 1 + * Byte 3 = 0 + */ +#ifdef XK_LATIN1 +#define XK_space 0x020 +#define XK_exclam 0x021 +#define XK_quotedbl 0x022 +#define XK_numbersign 0x023 +#define XK_dollar 0x024 +#define XK_percent 0x025 +#define XK_ampersand 0x026 +#define XK_apostrophe 0x027 +#define XK_quoteright 0x027 /* deprecated */ +#define XK_parenleft 0x028 +#define XK_parenright 0x029 +#define XK_asterisk 0x02a +#define XK_plus 0x02b +#define XK_comma 0x02c +#define XK_minus 0x02d +#define XK_period 0x02e +#define XK_slash 0x02f +#define XK_0 0x030 +#define XK_1 0x031 +#define XK_2 0x032 +#define XK_3 0x033 +#define XK_4 0x034 +#define XK_5 0x035 +#define XK_6 0x036 +#define XK_7 0x037 +#define XK_8 0x038 +#define XK_9 0x039 +#define XK_colon 0x03a +#define XK_semicolon 0x03b +#define XK_less 0x03c +#define XK_equal 0x03d +#define XK_greater 0x03e +#define XK_question 0x03f +#define XK_at 0x040 +#define XK_A 0x041 +#define XK_B 0x042 +#define XK_C 0x043 +#define XK_D 0x044 +#define XK_E 0x045 +#define XK_F 0x046 +#define XK_G 0x047 +#define XK_H 0x048 +#define XK_I 0x049 +#define XK_J 0x04a +#define XK_K 0x04b +#define XK_L 0x04c +#define XK_M 0x04d +#define XK_N 0x04e +#define XK_O 0x04f +#define XK_P 0x050 +#define XK_Q 0x051 +#define XK_R 0x052 +#define XK_S 0x053 +#define XK_T 0x054 +#define XK_U 0x055 +#define XK_V 0x056 +#define XK_W 0x057 +#define XK_X 0x058 +#define XK_Y 0x059 +#define XK_Z 0x05a +#define XK_bracketleft 0x05b +#define XK_backslash 0x05c +#define XK_bracketright 0x05d +#define XK_asciicircum 0x05e +#define XK_underscore 0x05f +#define XK_grave 0x060 +#define XK_quoteleft 0x060 /* deprecated */ +#define XK_a 0x061 +#define XK_b 0x062 +#define XK_c 0x063 +#define XK_d 0x064 +#define XK_e 0x065 +#define XK_f 0x066 +#define XK_g 0x067 +#define XK_h 0x068 +#define XK_i 0x069 +#define XK_j 0x06a +#define XK_k 0x06b +#define XK_l 0x06c +#define XK_m 0x06d +#define XK_n 0x06e +#define XK_o 0x06f +#define XK_p 0x070 +#define XK_q 0x071 +#define XK_r 0x072 +#define XK_s 0x073 +#define XK_t 0x074 +#define XK_u 0x075 +#define XK_v 0x076 +#define XK_w 0x077 +#define XK_x 0x078 +#define XK_y 0x079 +#define XK_z 0x07a +#define XK_braceleft 0x07b +#define XK_bar 0x07c +#define XK_braceright 0x07d +#define XK_asciitilde 0x07e + +#define XK_nobreakspace 0x0a0 +#define XK_exclamdown 0x0a1 +#define XK_cent 0x0a2 +#define XK_sterling 0x0a3 +#define XK_currency 0x0a4 +#define XK_yen 0x0a5 +#define XK_brokenbar 0x0a6 +#define XK_section 0x0a7 +#define XK_diaeresis 0x0a8 +#define XK_copyright 0x0a9 +#define XK_ordfeminine 0x0aa +#define XK_guillemotleft 0x0ab /* left angle quotation mark */ +#define XK_notsign 0x0ac +#define XK_hyphen 0x0ad +#define XK_registered 0x0ae +#define XK_macron 0x0af +#define XK_degree 0x0b0 +#define XK_plusminus 0x0b1 +#define XK_twosuperior 0x0b2 +#define XK_threesuperior 0x0b3 +#define XK_acute 0x0b4 +#define XK_mu 0x0b5 +#define XK_paragraph 0x0b6 +#define XK_periodcentered 0x0b7 +#define XK_cedilla 0x0b8 +#define XK_onesuperior 0x0b9 +#define XK_masculine 0x0ba +#define XK_guillemotright 0x0bb /* right angle quotation mark */ +#define XK_onequarter 0x0bc +#define XK_onehalf 0x0bd +#define XK_threequarters 0x0be +#define XK_questiondown 0x0bf +#define XK_Agrave 0x0c0 +#define XK_Aacute 0x0c1 +#define XK_Acircumflex 0x0c2 +#define XK_Atilde 0x0c3 +#define XK_Adiaeresis 0x0c4 +#define XK_Aring 0x0c5 +#define XK_AE 0x0c6 +#define XK_Ccedilla 0x0c7 +#define XK_Egrave 0x0c8 +#define XK_Eacute 0x0c9 +#define XK_Ecircumflex 0x0ca +#define XK_Ediaeresis 0x0cb +#define XK_Igrave 0x0cc +#define XK_Iacute 0x0cd +#define XK_Icircumflex 0x0ce +#define XK_Idiaeresis 0x0cf +#define XK_ETH 0x0d0 +#define XK_Eth 0x0d0 /* deprecated */ +#define XK_Ntilde 0x0d1 +#define XK_Ograve 0x0d2 +#define XK_Oacute 0x0d3 +#define XK_Ocircumflex 0x0d4 +#define XK_Otilde 0x0d5 +#define XK_Odiaeresis 0x0d6 +#define XK_multiply 0x0d7 +#define XK_Ooblique 0x0d8 +#define XK_Ugrave 0x0d9 +#define XK_Uacute 0x0da +#define XK_Ucircumflex 0x0db +#define XK_Udiaeresis 0x0dc +#define XK_Yacute 0x0dd +#define XK_THORN 0x0de +#define XK_Thorn 0x0de /* deprecated */ +#define XK_ssharp 0x0df +#define XK_agrave 0x0e0 +#define XK_aacute 0x0e1 +#define XK_acircumflex 0x0e2 +#define XK_atilde 0x0e3 +#define XK_adiaeresis 0x0e4 +#define XK_aring 0x0e5 +#define XK_ae 0x0e6 +#define XK_ccedilla 0x0e7 +#define XK_egrave 0x0e8 +#define XK_eacute 0x0e9 +#define XK_ecircumflex 0x0ea +#define XK_ediaeresis 0x0eb +#define XK_igrave 0x0ec +#define XK_iacute 0x0ed +#define XK_icircumflex 0x0ee +#define XK_idiaeresis 0x0ef +#define XK_eth 0x0f0 +#define XK_ntilde 0x0f1 +#define XK_ograve 0x0f2 +#define XK_oacute 0x0f3 +#define XK_ocircumflex 0x0f4 +#define XK_otilde 0x0f5 +#define XK_odiaeresis 0x0f6 +#define XK_division 0x0f7 +#define XK_oslash 0x0f8 +#define XK_ugrave 0x0f9 +#define XK_uacute 0x0fa +#define XK_ucircumflex 0x0fb +#define XK_udiaeresis 0x0fc +#define XK_yacute 0x0fd +#define XK_thorn 0x0fe +#define XK_ydiaeresis 0x0ff +#endif /* XK_LATIN1 */ + +/* + * Latin 2 + * Byte 3 = 1 + */ + +#ifdef XK_LATIN2 +#define XK_Aogonek 0x1a1 +#define XK_breve 0x1a2 +#define XK_Lstroke 0x1a3 +#define XK_Lcaron 0x1a5 +#define XK_Sacute 0x1a6 +#define XK_Scaron 0x1a9 +#define XK_Scedilla 0x1aa +#define XK_Tcaron 0x1ab +#define XK_Zacute 0x1ac +#define XK_Zcaron 0x1ae +#define XK_Zabovedot 0x1af +#define XK_aogonek 0x1b1 +#define XK_ogonek 0x1b2 +#define XK_lstroke 0x1b3 +#define XK_lcaron 0x1b5 +#define XK_sacute 0x1b6 +#define XK_caron 0x1b7 +#define XK_scaron 0x1b9 +#define XK_scedilla 0x1ba +#define XK_tcaron 0x1bb +#define XK_zacute 0x1bc +#define XK_doubleacute 0x1bd +#define XK_zcaron 0x1be +#define XK_zabovedot 0x1bf +#define XK_Racute 0x1c0 +#define XK_Abreve 0x1c3 +#define XK_Lacute 0x1c5 +#define XK_Cacute 0x1c6 +#define XK_Ccaron 0x1c8 +#define XK_Eogonek 0x1ca +#define XK_Ecaron 0x1cc +#define XK_Dcaron 0x1cf +#define XK_Dstroke 0x1d0 +#define XK_Nacute 0x1d1 +#define XK_Ncaron 0x1d2 +#define XK_Odoubleacute 0x1d5 +#define XK_Rcaron 0x1d8 +#define XK_Uring 0x1d9 +#define XK_Udoubleacute 0x1db +#define XK_Tcedilla 0x1de +#define XK_racute 0x1e0 +#define XK_abreve 0x1e3 +#define XK_lacute 0x1e5 +#define XK_cacute 0x1e6 +#define XK_ccaron 0x1e8 +#define XK_eogonek 0x1ea +#define XK_ecaron 0x1ec +#define XK_dcaron 0x1ef +#define XK_dstroke 0x1f0 +#define XK_nacute 0x1f1 +#define XK_ncaron 0x1f2 +#define XK_odoubleacute 0x1f5 +#define XK_udoubleacute 0x1fb +#define XK_rcaron 0x1f8 +#define XK_uring 0x1f9 +#define XK_tcedilla 0x1fe +#define XK_abovedot 0x1ff +#endif /* XK_LATIN2 */ + +/* + * Latin 3 + * Byte 3 = 2 + */ + +#ifdef XK_LATIN3 +#define XK_Hstroke 0x2a1 +#define XK_Hcircumflex 0x2a6 +#define XK_Iabovedot 0x2a9 +#define XK_Gbreve 0x2ab +#define XK_Jcircumflex 0x2ac +#define XK_hstroke 0x2b1 +#define XK_hcircumflex 0x2b6 +#define XK_idotless 0x2b9 +#define XK_gbreve 0x2bb +#define XK_jcircumflex 0x2bc +#define XK_Cabovedot 0x2c5 +#define XK_Ccircumflex 0x2c6 +#define XK_Gabovedot 0x2d5 +#define XK_Gcircumflex 0x2d8 +#define XK_Ubreve 0x2dd +#define XK_Scircumflex 0x2de +#define XK_cabovedot 0x2e5 +#define XK_ccircumflex 0x2e6 +#define XK_gabovedot 0x2f5 +#define XK_gcircumflex 0x2f8 +#define XK_ubreve 0x2fd +#define XK_scircumflex 0x2fe +#endif /* XK_LATIN3 */ + + +/* + * Latin 4 + * Byte 3 = 3 + */ + +#ifdef XK_LATIN4 +#define XK_kra 0x3a2 +#define XK_kappa 0x3a2 /* deprecated */ +#define XK_Rcedilla 0x3a3 +#define XK_Itilde 0x3a5 +#define XK_Lcedilla 0x3a6 +#define XK_Emacron 0x3aa +#define XK_Gcedilla 0x3ab +#define XK_Tslash 0x3ac +#define XK_rcedilla 0x3b3 +#define XK_itilde 0x3b5 +#define XK_lcedilla 0x3b6 +#define XK_emacron 0x3ba +#define XK_gcedilla 0x3bb +#define XK_tslash 0x3bc +#define XK_ENG 0x3bd +#define XK_eng 0x3bf +#define XK_Amacron 0x3c0 +#define XK_Iogonek 0x3c7 +#define XK_Eabovedot 0x3cc +#define XK_Imacron 0x3cf +#define XK_Ncedilla 0x3d1 +#define XK_Omacron 0x3d2 +#define XK_Kcedilla 0x3d3 +#define XK_Uogonek 0x3d9 +#define XK_Utilde 0x3dd +#define XK_Umacron 0x3de +#define XK_amacron 0x3e0 +#define XK_iogonek 0x3e7 +#define XK_eabovedot 0x3ec +#define XK_imacron 0x3ef +#define XK_ncedilla 0x3f1 +#define XK_omacron 0x3f2 +#define XK_kcedilla 0x3f3 +#define XK_uogonek 0x3f9 +#define XK_utilde 0x3fd +#define XK_umacron 0x3fe +#endif /* XK_LATIN4 */ + +/* + * Latin-9 (a.k.a. Latin-0) + * Byte 3 = 19 + */ + +#ifdef XK_LATIN9 +#define XK_OE 0x13bc +#define XK_oe 0x13bd +#define XK_Ydiaeresis 0x13be +#endif /* XK_LATIN9 */ + +/* + * Katakana + * Byte 3 = 4 + */ + +#ifdef XK_KATAKANA +#define XK_overline 0x47e +#define XK_kana_fullstop 0x4a1 +#define XK_kana_openingbracket 0x4a2 +#define XK_kana_closingbracket 0x4a3 +#define XK_kana_comma 0x4a4 +#define XK_kana_conjunctive 0x4a5 +#define XK_kana_middledot 0x4a5 /* deprecated */ +#define XK_kana_WO 0x4a6 +#define XK_kana_a 0x4a7 +#define XK_kana_i 0x4a8 +#define XK_kana_u 0x4a9 +#define XK_kana_e 0x4aa +#define XK_kana_o 0x4ab +#define XK_kana_ya 0x4ac +#define XK_kana_yu 0x4ad +#define XK_kana_yo 0x4ae +#define XK_kana_tsu 0x4af +#define XK_kana_tu 0x4af /* deprecated */ +#define XK_prolongedsound 0x4b0 +#define XK_kana_A 0x4b1 +#define XK_kana_I 0x4b2 +#define XK_kana_U 0x4b3 +#define XK_kana_E 0x4b4 +#define XK_kana_O 0x4b5 +#define XK_kana_KA 0x4b6 +#define XK_kana_KI 0x4b7 +#define XK_kana_KU 0x4b8 +#define XK_kana_KE 0x4b9 +#define XK_kana_KO 0x4ba +#define XK_kana_SA 0x4bb +#define XK_kana_SHI 0x4bc +#define XK_kana_SU 0x4bd +#define XK_kana_SE 0x4be +#define XK_kana_SO 0x4bf +#define XK_kana_TA 0x4c0 +#define XK_kana_CHI 0x4c1 +#define XK_kana_TI 0x4c1 /* deprecated */ +#define XK_kana_TSU 0x4c2 +#define XK_kana_TU 0x4c2 /* deprecated */ +#define XK_kana_TE 0x4c3 +#define XK_kana_TO 0x4c4 +#define XK_kana_NA 0x4c5 +#define XK_kana_NI 0x4c6 +#define XK_kana_NU 0x4c7 +#define XK_kana_NE 0x4c8 +#define XK_kana_NO 0x4c9 +#define XK_kana_HA 0x4ca +#define XK_kana_HI 0x4cb +#define XK_kana_FU 0x4cc +#define XK_kana_HU 0x4cc /* deprecated */ +#define XK_kana_HE 0x4cd +#define XK_kana_HO 0x4ce +#define XK_kana_MA 0x4cf +#define XK_kana_MI 0x4d0 +#define XK_kana_MU 0x4d1 +#define XK_kana_ME 0x4d2 +#define XK_kana_MO 0x4d3 +#define XK_kana_YA 0x4d4 +#define XK_kana_YU 0x4d5 +#define XK_kana_YO 0x4d6 +#define XK_kana_RA 0x4d7 +#define XK_kana_RI 0x4d8 +#define XK_kana_RU 0x4d9 +#define XK_kana_RE 0x4da +#define XK_kana_RO 0x4db +#define XK_kana_WA 0x4dc +#define XK_kana_N 0x4dd +#define XK_voicedsound 0x4de +#define XK_semivoicedsound 0x4df +#define XK_kana_switch 0xFF7E /* Alias for mode_switch */ +#endif /* XK_KATAKANA */ + +/* + * Arabic + * Byte 3 = 5 + */ + +#ifdef XK_ARABIC +#define XK_Arabic_comma 0x5ac +#define XK_Arabic_semicolon 0x5bb +#define XK_Arabic_question_mark 0x5bf +#define XK_Arabic_hamza 0x5c1 +#define XK_Arabic_maddaonalef 0x5c2 +#define XK_Arabic_hamzaonalef 0x5c3 +#define XK_Arabic_hamzaonwaw 0x5c4 +#define XK_Arabic_hamzaunderalef 0x5c5 +#define XK_Arabic_hamzaonyeh 0x5c6 +#define XK_Arabic_alef 0x5c7 +#define XK_Arabic_beh 0x5c8 +#define XK_Arabic_tehmarbuta 0x5c9 +#define XK_Arabic_teh 0x5ca +#define XK_Arabic_theh 0x5cb +#define XK_Arabic_jeem 0x5cc +#define XK_Arabic_hah 0x5cd +#define XK_Arabic_khah 0x5ce +#define XK_Arabic_dal 0x5cf +#define XK_Arabic_thal 0x5d0 +#define XK_Arabic_ra 0x5d1 +#define XK_Arabic_zain 0x5d2 +#define XK_Arabic_seen 0x5d3 +#define XK_Arabic_sheen 0x5d4 +#define XK_Arabic_sad 0x5d5 +#define XK_Arabic_dad 0x5d6 +#define XK_Arabic_tah 0x5d7 +#define XK_Arabic_zah 0x5d8 +#define XK_Arabic_ain 0x5d9 +#define XK_Arabic_ghain 0x5da +#define XK_Arabic_tatweel 0x5e0 +#define XK_Arabic_feh 0x5e1 +#define XK_Arabic_qaf 0x5e2 +#define XK_Arabic_kaf 0x5e3 +#define XK_Arabic_lam 0x5e4 +#define XK_Arabic_meem 0x5e5 +#define XK_Arabic_noon 0x5e6 +#define XK_Arabic_ha 0x5e7 +#define XK_Arabic_heh 0x5e7 /* deprecated */ +#define XK_Arabic_waw 0x5e8 +#define XK_Arabic_alefmaksura 0x5e9 +#define XK_Arabic_yeh 0x5ea +#define XK_Arabic_fathatan 0x5eb +#define XK_Arabic_dammatan 0x5ec +#define XK_Arabic_kasratan 0x5ed +#define XK_Arabic_fatha 0x5ee +#define XK_Arabic_damma 0x5ef +#define XK_Arabic_kasra 0x5f0 +#define XK_Arabic_shadda 0x5f1 +#define XK_Arabic_sukun 0x5f2 +#define XK_Arabic_switch 0xFF7E /* Alias for mode_switch */ +#endif /* XK_ARABIC */ + +/* + * Cyrillic + * Byte 3 = 6 + */ +#ifdef XK_CYRILLIC +#define XK_Serbian_dje 0x6a1 +#define XK_Macedonia_gje 0x6a2 +#define XK_Cyrillic_io 0x6a3 +#define XK_Ukrainian_ie 0x6a4 +#define XK_Ukranian_je 0x6a4 /* deprecated */ +#define XK_Macedonia_dse 0x6a5 +#define XK_Ukrainian_i 0x6a6 +#define XK_Ukranian_i 0x6a6 /* deprecated */ +#define XK_Ukrainian_yi 0x6a7 +#define XK_Ukranian_yi 0x6a7 /* deprecated */ +#define XK_Cyrillic_je 0x6a8 +#define XK_Serbian_je 0x6a8 /* deprecated */ +#define XK_Cyrillic_lje 0x6a9 +#define XK_Serbian_lje 0x6a9 /* deprecated */ +#define XK_Cyrillic_nje 0x6aa +#define XK_Serbian_nje 0x6aa /* deprecated */ +#define XK_Serbian_tshe 0x6ab +#define XK_Macedonia_kje 0x6ac +#define XK_Byelorussian_shortu 0x6ae +#define XK_Cyrillic_dzhe 0x6af +#define XK_Serbian_dze 0x6af /* deprecated */ +#define XK_numerosign 0x6b0 +#define XK_Serbian_DJE 0x6b1 +#define XK_Macedonia_GJE 0x6b2 +#define XK_Cyrillic_IO 0x6b3 +#define XK_Ukrainian_IE 0x6b4 +#define XK_Ukranian_JE 0x6b4 /* deprecated */ +#define XK_Macedonia_DSE 0x6b5 +#define XK_Ukrainian_I 0x6b6 +#define XK_Ukranian_I 0x6b6 /* deprecated */ +#define XK_Ukrainian_YI 0x6b7 +#define XK_Ukranian_YI 0x6b7 /* deprecated */ +#define XK_Cyrillic_JE 0x6b8 +#define XK_Serbian_JE 0x6b8 /* deprecated */ +#define XK_Cyrillic_LJE 0x6b9 +#define XK_Serbian_LJE 0x6b9 /* deprecated */ +#define XK_Cyrillic_NJE 0x6ba +#define XK_Serbian_NJE 0x6ba /* deprecated */ +#define XK_Serbian_TSHE 0x6bb +#define XK_Macedonia_KJE 0x6bc +#define XK_Byelorussian_SHORTU 0x6be +#define XK_Cyrillic_DZHE 0x6bf +#define XK_Serbian_DZE 0x6bf /* deprecated */ +#define XK_Cyrillic_yu 0x6c0 +#define XK_Cyrillic_a 0x6c1 +#define XK_Cyrillic_be 0x6c2 +#define XK_Cyrillic_tse 0x6c3 +#define XK_Cyrillic_de 0x6c4 +#define XK_Cyrillic_ie 0x6c5 +#define XK_Cyrillic_ef 0x6c6 +#define XK_Cyrillic_ghe 0x6c7 +#define XK_Cyrillic_ha 0x6c8 +#define XK_Cyrillic_i 0x6c9 +#define XK_Cyrillic_shorti 0x6ca +#define XK_Cyrillic_ka 0x6cb +#define XK_Cyrillic_el 0x6cc +#define XK_Cyrillic_em 0x6cd +#define XK_Cyrillic_en 0x6ce +#define XK_Cyrillic_o 0x6cf +#define XK_Cyrillic_pe 0x6d0 +#define XK_Cyrillic_ya 0x6d1 +#define XK_Cyrillic_er 0x6d2 +#define XK_Cyrillic_es 0x6d3 +#define XK_Cyrillic_te 0x6d4 +#define XK_Cyrillic_u 0x6d5 +#define XK_Cyrillic_zhe 0x6d6 +#define XK_Cyrillic_ve 0x6d7 +#define XK_Cyrillic_softsign 0x6d8 +#define XK_Cyrillic_yeru 0x6d9 +#define XK_Cyrillic_ze 0x6da +#define XK_Cyrillic_sha 0x6db +#define XK_Cyrillic_e 0x6dc +#define XK_Cyrillic_shcha 0x6dd +#define XK_Cyrillic_che 0x6de +#define XK_Cyrillic_hardsign 0x6df +#define XK_Cyrillic_YU 0x6e0 +#define XK_Cyrillic_A 0x6e1 +#define XK_Cyrillic_BE 0x6e2 +#define XK_Cyrillic_TSE 0x6e3 +#define XK_Cyrillic_DE 0x6e4 +#define XK_Cyrillic_IE 0x6e5 +#define XK_Cyrillic_EF 0x6e6 +#define XK_Cyrillic_GHE 0x6e7 +#define XK_Cyrillic_HA 0x6e8 +#define XK_Cyrillic_I 0x6e9 +#define XK_Cyrillic_SHORTI 0x6ea +#define XK_Cyrillic_KA 0x6eb +#define XK_Cyrillic_EL 0x6ec +#define XK_Cyrillic_EM 0x6ed +#define XK_Cyrillic_EN 0x6ee +#define XK_Cyrillic_O 0x6ef +#define XK_Cyrillic_PE 0x6f0 +#define XK_Cyrillic_YA 0x6f1 +#define XK_Cyrillic_ER 0x6f2 +#define XK_Cyrillic_ES 0x6f3 +#define XK_Cyrillic_TE 0x6f4 +#define XK_Cyrillic_U 0x6f5 +#define XK_Cyrillic_ZHE 0x6f6 +#define XK_Cyrillic_VE 0x6f7 +#define XK_Cyrillic_SOFTSIGN 0x6f8 +#define XK_Cyrillic_YERU 0x6f9 +#define XK_Cyrillic_ZE 0x6fa +#define XK_Cyrillic_SHA 0x6fb +#define XK_Cyrillic_E 0x6fc +#define XK_Cyrillic_SHCHA 0x6fd +#define XK_Cyrillic_CHE 0x6fe +#define XK_Cyrillic_HARDSIGN 0x6ff +#endif /* XK_CYRILLIC */ + +/* + * Greek + * Byte 3 = 7 + */ + +#ifdef XK_GREEK +#define XK_Greek_ALPHAaccent 0x7a1 +#define XK_Greek_EPSILONaccent 0x7a2 +#define XK_Greek_ETAaccent 0x7a3 +#define XK_Greek_IOTAaccent 0x7a4 +#define XK_Greek_IOTAdiaeresis 0x7a5 +#define XK_Greek_OMICRONaccent 0x7a7 +#define XK_Greek_UPSILONaccent 0x7a8 +#define XK_Greek_UPSILONdieresis 0x7a9 +#define XK_Greek_OMEGAaccent 0x7ab +#define XK_Greek_accentdieresis 0x7ae +#define XK_Greek_horizbar 0x7af +#define XK_Greek_alphaaccent 0x7b1 +#define XK_Greek_epsilonaccent 0x7b2 +#define XK_Greek_etaaccent 0x7b3 +#define XK_Greek_iotaaccent 0x7b4 +#define XK_Greek_iotadieresis 0x7b5 +#define XK_Greek_iotaaccentdieresis 0x7b6 +#define XK_Greek_omicronaccent 0x7b7 +#define XK_Greek_upsilonaccent 0x7b8 +#define XK_Greek_upsilondieresis 0x7b9 +#define XK_Greek_upsilonaccentdieresis 0x7ba +#define XK_Greek_omegaaccent 0x7bb +#define XK_Greek_ALPHA 0x7c1 +#define XK_Greek_BETA 0x7c2 +#define XK_Greek_GAMMA 0x7c3 +#define XK_Greek_DELTA 0x7c4 +#define XK_Greek_EPSILON 0x7c5 +#define XK_Greek_ZETA 0x7c6 +#define XK_Greek_ETA 0x7c7 +#define XK_Greek_THETA 0x7c8 +#define XK_Greek_IOTA 0x7c9 +#define XK_Greek_KAPPA 0x7ca +#define XK_Greek_LAMDA 0x7cb +#define XK_Greek_LAMBDA 0x7cb +#define XK_Greek_MU 0x7cc +#define XK_Greek_NU 0x7cd +#define XK_Greek_XI 0x7ce +#define XK_Greek_OMICRON 0x7cf +#define XK_Greek_PI 0x7d0 +#define XK_Greek_RHO 0x7d1 +#define XK_Greek_SIGMA 0x7d2 +#define XK_Greek_TAU 0x7d4 +#define XK_Greek_UPSILON 0x7d5 +#define XK_Greek_PHI 0x7d6 +#define XK_Greek_CHI 0x7d7 +#define XK_Greek_PSI 0x7d8 +#define XK_Greek_OMEGA 0x7d9 +#define XK_Greek_alpha 0x7e1 +#define XK_Greek_beta 0x7e2 +#define XK_Greek_gamma 0x7e3 +#define XK_Greek_delta 0x7e4 +#define XK_Greek_epsilon 0x7e5 +#define XK_Greek_zeta 0x7e6 +#define XK_Greek_eta 0x7e7 +#define XK_Greek_theta 0x7e8 +#define XK_Greek_iota 0x7e9 +#define XK_Greek_kappa 0x7ea +#define XK_Greek_lamda 0x7eb +#define XK_Greek_lambda 0x7eb +#define XK_Greek_mu 0x7ec +#define XK_Greek_nu 0x7ed +#define XK_Greek_xi 0x7ee +#define XK_Greek_omicron 0x7ef +#define XK_Greek_pi 0x7f0 +#define XK_Greek_rho 0x7f1 +#define XK_Greek_sigma 0x7f2 +#define XK_Greek_finalsmallsigma 0x7f3 +#define XK_Greek_tau 0x7f4 +#define XK_Greek_upsilon 0x7f5 +#define XK_Greek_phi 0x7f6 +#define XK_Greek_chi 0x7f7 +#define XK_Greek_psi 0x7f8 +#define XK_Greek_omega 0x7f9 +#define XK_Greek_switch 0xFF7E /* Alias for mode_switch */ +#endif /* XK_GREEK */ + +/* + * Technical + * Byte 3 = 8 + */ + +#ifdef XK_TECHNICAL +#define XK_leftradical 0x8a1 +#define XK_topleftradical 0x8a2 +#define XK_horizconnector 0x8a3 +#define XK_topintegral 0x8a4 +#define XK_botintegral 0x8a5 +#define XK_vertconnector 0x8a6 +#define XK_topleftsqbracket 0x8a7 +#define XK_botleftsqbracket 0x8a8 +#define XK_toprightsqbracket 0x8a9 +#define XK_botrightsqbracket 0x8aa +#define XK_topleftparens 0x8ab +#define XK_botleftparens 0x8ac +#define XK_toprightparens 0x8ad +#define XK_botrightparens 0x8ae +#define XK_leftmiddlecurlybrace 0x8af +#define XK_rightmiddlecurlybrace 0x8b0 +#define XK_topleftsummation 0x8b1 +#define XK_botleftsummation 0x8b2 +#define XK_topvertsummationconnector 0x8b3 +#define XK_botvertsummationconnector 0x8b4 +#define XK_toprightsummation 0x8b5 +#define XK_botrightsummation 0x8b6 +#define XK_rightmiddlesummation 0x8b7 +#define XK_lessthanequal 0x8bc +#define XK_notequal 0x8bd +#define XK_greaterthanequal 0x8be +#define XK_integral 0x8bf +#define XK_therefore 0x8c0 +#define XK_variation 0x8c1 +#define XK_infinity 0x8c2 +#define XK_nabla 0x8c5 +#define XK_approximate 0x8c8 +#define XK_similarequal 0x8c9 +#define XK_ifonlyif 0x8cd +#define XK_implies 0x8ce +#define XK_identical 0x8cf +#define XK_radical 0x8d6 +#define XK_includedin 0x8da +#define XK_includes 0x8db +#define XK_intersection 0x8dc +#define XK_union 0x8dd +#define XK_logicaland 0x8de +#define XK_logicalor 0x8df +#define XK_partialderivative 0x8ef +#define XK_function 0x8f6 +#define XK_leftarrow 0x8fb +#define XK_uparrow 0x8fc +#define XK_rightarrow 0x8fd +#define XK_downarrow 0x8fe +#endif /* XK_TECHNICAL */ + +/* + * Special + * Byte 3 = 9 + */ + +#ifdef XK_SPECIAL +#define XK_blank 0x9df +#define XK_soliddiamond 0x9e0 +#define XK_checkerboard 0x9e1 +#define XK_ht 0x9e2 +#define XK_ff 0x9e3 +#define XK_cr 0x9e4 +#define XK_lf 0x9e5 +#define XK_nl 0x9e8 +#define XK_vt 0x9e9 +#define XK_lowrightcorner 0x9ea +#define XK_uprightcorner 0x9eb +#define XK_upleftcorner 0x9ec +#define XK_lowleftcorner 0x9ed +#define XK_crossinglines 0x9ee +#define XK_horizlinescan1 0x9ef +#define XK_horizlinescan3 0x9f0 +#define XK_horizlinescan5 0x9f1 +#define XK_horizlinescan7 0x9f2 +#define XK_horizlinescan9 0x9f3 +#define XK_leftt 0x9f4 +#define XK_rightt 0x9f5 +#define XK_bott 0x9f6 +#define XK_topt 0x9f7 +#define XK_vertbar 0x9f8 +#endif /* XK_SPECIAL */ + +/* + * Publishing + * Byte 3 = a + */ + +#ifdef XK_PUBLISHING +#define XK_emspace 0xaa1 +#define XK_enspace 0xaa2 +#define XK_em3space 0xaa3 +#define XK_em4space 0xaa4 +#define XK_digitspace 0xaa5 +#define XK_punctspace 0xaa6 +#define XK_thinspace 0xaa7 +#define XK_hairspace 0xaa8 +#define XK_emdash 0xaa9 +#define XK_endash 0xaaa +#define XK_signifblank 0xaac +#define XK_ellipsis 0xaae +#define XK_doubbaselinedot 0xaaf +#define XK_onethird 0xab0 +#define XK_twothirds 0xab1 +#define XK_onefifth 0xab2 +#define XK_twofifths 0xab3 +#define XK_threefifths 0xab4 +#define XK_fourfifths 0xab5 +#define XK_onesixth 0xab6 +#define XK_fivesixths 0xab7 +#define XK_careof 0xab8 +#define XK_figdash 0xabb +#define XK_leftanglebracket 0xabc +#define XK_decimalpoint 0xabd +#define XK_rightanglebracket 0xabe +#define XK_marker 0xabf +#define XK_oneeighth 0xac3 +#define XK_threeeighths 0xac4 +#define XK_fiveeighths 0xac5 +#define XK_seveneighths 0xac6 +#define XK_trademark 0xac9 +#define XK_signaturemark 0xaca +#define XK_trademarkincircle 0xacb +#define XK_leftopentriangle 0xacc +#define XK_rightopentriangle 0xacd +#define XK_emopencircle 0xace +#define XK_emopenrectangle 0xacf +#define XK_leftsinglequotemark 0xad0 +#define XK_rightsinglequotemark 0xad1 +#define XK_leftdoublequotemark 0xad2 +#define XK_rightdoublequotemark 0xad3 +#define XK_prescription 0xad4 +#define XK_minutes 0xad6 +#define XK_seconds 0xad7 +#define XK_latincross 0xad9 +#define XK_hexagram 0xada +#define XK_filledrectbullet 0xadb +#define XK_filledlefttribullet 0xadc +#define XK_filledrighttribullet 0xadd +#define XK_emfilledcircle 0xade +#define XK_emfilledrect 0xadf +#define XK_enopencircbullet 0xae0 +#define XK_enopensquarebullet 0xae1 +#define XK_openrectbullet 0xae2 +#define XK_opentribulletup 0xae3 +#define XK_opentribulletdown 0xae4 +#define XK_openstar 0xae5 +#define XK_enfilledcircbullet 0xae6 +#define XK_enfilledsqbullet 0xae7 +#define XK_filledtribulletup 0xae8 +#define XK_filledtribulletdown 0xae9 +#define XK_leftpointer 0xaea +#define XK_rightpointer 0xaeb +#define XK_club 0xaec +#define XK_diamond 0xaed +#define XK_heart 0xaee +#define XK_maltesecross 0xaf0 +#define XK_dagger 0xaf1 +#define XK_doubledagger 0xaf2 +#define XK_checkmark 0xaf3 +#define XK_ballotcross 0xaf4 +#define XK_musicalsharp 0xaf5 +#define XK_musicalflat 0xaf6 +#define XK_malesymbol 0xaf7 +#define XK_femalesymbol 0xaf8 +#define XK_telephone 0xaf9 +#define XK_telephonerecorder 0xafa +#define XK_phonographcopyright 0xafb +#define XK_caret 0xafc +#define XK_singlelowquotemark 0xafd +#define XK_doublelowquotemark 0xafe +#define XK_cursor 0xaff +#endif /* XK_PUBLISHING */ + +/* + * APL + * Byte 3 = b + */ + +#ifdef XK_APL +#define XK_leftcaret 0xba3 +#define XK_rightcaret 0xba6 +#define XK_downcaret 0xba8 +#define XK_upcaret 0xba9 +#define XK_overbar 0xbc0 +#define XK_downtack 0xbc2 +#define XK_upshoe 0xbc3 +#define XK_downstile 0xbc4 +#define XK_underbar 0xbc6 +#define XK_jot 0xbca +#define XK_quad 0xbcc +#define XK_uptack 0xbce +#define XK_circle 0xbcf +#define XK_upstile 0xbd3 +#define XK_downshoe 0xbd6 +#define XK_rightshoe 0xbd8 +#define XK_leftshoe 0xbda +#define XK_lefttack 0xbdc +#define XK_righttack 0xbfc +#endif /* XK_APL */ + +/* + * Hebrew + * Byte 3 = c + */ + +#ifdef XK_HEBREW +#define XK_hebrew_doublelowline 0xcdf +#define XK_hebrew_aleph 0xce0 +#define XK_hebrew_bet 0xce1 +#define XK_hebrew_beth 0xce1 /* deprecated */ +#define XK_hebrew_gimel 0xce2 +#define XK_hebrew_gimmel 0xce2 /* deprecated */ +#define XK_hebrew_dalet 0xce3 +#define XK_hebrew_daleth 0xce3 /* deprecated */ +#define XK_hebrew_he 0xce4 +#define XK_hebrew_waw 0xce5 +#define XK_hebrew_zain 0xce6 +#define XK_hebrew_zayin 0xce6 /* deprecated */ +#define XK_hebrew_chet 0xce7 +#define XK_hebrew_het 0xce7 /* deprecated */ +#define XK_hebrew_tet 0xce8 +#define XK_hebrew_teth 0xce8 /* deprecated */ +#define XK_hebrew_yod 0xce9 +#define XK_hebrew_finalkaph 0xcea +#define XK_hebrew_kaph 0xceb +#define XK_hebrew_lamed 0xcec +#define XK_hebrew_finalmem 0xced +#define XK_hebrew_mem 0xcee +#define XK_hebrew_finalnun 0xcef +#define XK_hebrew_nun 0xcf0 +#define XK_hebrew_samech 0xcf1 +#define XK_hebrew_samekh 0xcf1 /* deprecated */ +#define XK_hebrew_ayin 0xcf2 +#define XK_hebrew_finalpe 0xcf3 +#define XK_hebrew_pe 0xcf4 +#define XK_hebrew_finalzade 0xcf5 +#define XK_hebrew_finalzadi 0xcf5 /* deprecated */ +#define XK_hebrew_zade 0xcf6 +#define XK_hebrew_zadi 0xcf6 /* deprecated */ +#define XK_hebrew_qoph 0xcf7 +#define XK_hebrew_kuf 0xcf7 /* deprecated */ +#define XK_hebrew_resh 0xcf8 +#define XK_hebrew_shin 0xcf9 +#define XK_hebrew_taw 0xcfa +#define XK_hebrew_taf 0xcfa /* deprecated */ +#define XK_Hebrew_switch 0xFF7E /* Alias for mode_switch */ +#endif /* XK_HEBREW */ + +/* + * Thai + * Byte 3 = d + */ + +#ifdef XK_THAI +#define XK_Thai_kokai 0xda1 +#define XK_Thai_khokhai 0xda2 +#define XK_Thai_khokhuat 0xda3 +#define XK_Thai_khokhwai 0xda4 +#define XK_Thai_khokhon 0xda5 +#define XK_Thai_khorakhang 0xda6 +#define XK_Thai_ngongu 0xda7 +#define XK_Thai_chochan 0xda8 +#define XK_Thai_choching 0xda9 +#define XK_Thai_chochang 0xdaa +#define XK_Thai_soso 0xdab +#define XK_Thai_chochoe 0xdac +#define XK_Thai_yoying 0xdad +#define XK_Thai_dochada 0xdae +#define XK_Thai_topatak 0xdaf +#define XK_Thai_thothan 0xdb0 +#define XK_Thai_thonangmontho 0xdb1 +#define XK_Thai_thophuthao 0xdb2 +#define XK_Thai_nonen 0xdb3 +#define XK_Thai_dodek 0xdb4 +#define XK_Thai_totao 0xdb5 +#define XK_Thai_thothung 0xdb6 +#define XK_Thai_thothahan 0xdb7 +#define XK_Thai_thothong 0xdb8 +#define XK_Thai_nonu 0xdb9 +#define XK_Thai_bobaimai 0xdba +#define XK_Thai_popla 0xdbb +#define XK_Thai_phophung 0xdbc +#define XK_Thai_fofa 0xdbd +#define XK_Thai_phophan 0xdbe +#define XK_Thai_fofan 0xdbf +#define XK_Thai_phosamphao 0xdc0 +#define XK_Thai_moma 0xdc1 +#define XK_Thai_yoyak 0xdc2 +#define XK_Thai_rorua 0xdc3 +#define XK_Thai_ru 0xdc4 +#define XK_Thai_loling 0xdc5 +#define XK_Thai_lu 0xdc6 +#define XK_Thai_wowaen 0xdc7 +#define XK_Thai_sosala 0xdc8 +#define XK_Thai_sorusi 0xdc9 +#define XK_Thai_sosua 0xdca +#define XK_Thai_hohip 0xdcb +#define XK_Thai_lochula 0xdcc +#define XK_Thai_oang 0xdcd +#define XK_Thai_honokhuk 0xdce +#define XK_Thai_paiyannoi 0xdcf +#define XK_Thai_saraa 0xdd0 +#define XK_Thai_maihanakat 0xdd1 +#define XK_Thai_saraaa 0xdd2 +#define XK_Thai_saraam 0xdd3 +#define XK_Thai_sarai 0xdd4 +#define XK_Thai_saraii 0xdd5 +#define XK_Thai_saraue 0xdd6 +#define XK_Thai_sarauee 0xdd7 +#define XK_Thai_sarau 0xdd8 +#define XK_Thai_sarauu 0xdd9 +#define XK_Thai_phinthu 0xdda +#define XK_Thai_maihanakat_maitho 0xdde +#define XK_Thai_baht 0xddf +#define XK_Thai_sarae 0xde0 +#define XK_Thai_saraae 0xde1 +#define XK_Thai_sarao 0xde2 +#define XK_Thai_saraaimaimuan 0xde3 +#define XK_Thai_saraaimaimalai 0xde4 +#define XK_Thai_lakkhangyao 0xde5 +#define XK_Thai_maiyamok 0xde6 +#define XK_Thai_maitaikhu 0xde7 +#define XK_Thai_maiek 0xde8 +#define XK_Thai_maitho 0xde9 +#define XK_Thai_maitri 0xdea +#define XK_Thai_maichattawa 0xdeb +#define XK_Thai_thanthakhat 0xdec +#define XK_Thai_nikhahit 0xded +#define XK_Thai_leksun 0xdf0 +#define XK_Thai_leknung 0xdf1 +#define XK_Thai_leksong 0xdf2 +#define XK_Thai_leksam 0xdf3 +#define XK_Thai_leksi 0xdf4 +#define XK_Thai_lekha 0xdf5 +#define XK_Thai_lekhok 0xdf6 +#define XK_Thai_lekchet 0xdf7 +#define XK_Thai_lekpaet 0xdf8 +#define XK_Thai_lekkao 0xdf9 +#endif /* XK_THAI */ + +/* + * Korean + * Byte 3 = e + */ + +#ifdef XK_KOREAN + +#define XK_Hangul 0xff31 /* Hangul start/stop(toggle) */ +#define XK_Hangul_Start 0xff32 /* Hangul start */ +#define XK_Hangul_End 0xff33 /* Hangul end, English start */ +#define XK_Hangul_Hanja 0xff34 /* Start Hangul->Hanja Conversion */ +#define XK_Hangul_Jamo 0xff35 /* Hangul Jamo mode */ +#define XK_Hangul_Romaja 0xff36 /* Hangul Romaja mode */ +#define XK_Hangul_Codeinput 0xff37 /* Hangul code input mode */ +#define XK_Hangul_Jeonja 0xff38 /* Jeonja mode */ +#define XK_Hangul_Banja 0xff39 /* Banja mode */ +#define XK_Hangul_PreHanja 0xff3a /* Pre Hanja conversion */ +#define XK_Hangul_PostHanja 0xff3b /* Post Hanja conversion */ +#define XK_Hangul_SingleCandidate 0xff3c /* Single candidate */ +#define XK_Hangul_MultipleCandidate 0xff3d /* Multiple candidate */ +#define XK_Hangul_PreviousCandidate 0xff3e /* Previous candidate */ +#define XK_Hangul_Special 0xff3f /* Special symbols */ +#define XK_Hangul_switch 0xFF7E /* Alias for mode_switch */ + +/* Hangul Consonant Characters */ +#define XK_Hangul_Kiyeog 0xea1 +#define XK_Hangul_SsangKiyeog 0xea2 +#define XK_Hangul_KiyeogSios 0xea3 +#define XK_Hangul_Nieun 0xea4 +#define XK_Hangul_NieunJieuj 0xea5 +#define XK_Hangul_NieunHieuh 0xea6 +#define XK_Hangul_Dikeud 0xea7 +#define XK_Hangul_SsangDikeud 0xea8 +#define XK_Hangul_Rieul 0xea9 +#define XK_Hangul_RieulKiyeog 0xeaa +#define XK_Hangul_RieulMieum 0xeab +#define XK_Hangul_RieulPieub 0xeac +#define XK_Hangul_RieulSios 0xead +#define XK_Hangul_RieulTieut 0xeae +#define XK_Hangul_RieulPhieuf 0xeaf +#define XK_Hangul_RieulHieuh 0xeb0 +#define XK_Hangul_Mieum 0xeb1 +#define XK_Hangul_Pieub 0xeb2 +#define XK_Hangul_SsangPieub 0xeb3 +#define XK_Hangul_PieubSios 0xeb4 +#define XK_Hangul_Sios 0xeb5 +#define XK_Hangul_SsangSios 0xeb6 +#define XK_Hangul_Ieung 0xeb7 +#define XK_Hangul_Jieuj 0xeb8 +#define XK_Hangul_SsangJieuj 0xeb9 +#define XK_Hangul_Cieuc 0xeba +#define XK_Hangul_Khieuq 0xebb +#define XK_Hangul_Tieut 0xebc +#define XK_Hangul_Phieuf 0xebd +#define XK_Hangul_Hieuh 0xebe + +/* Hangul Vowel Characters */ +#define XK_Hangul_A 0xebf +#define XK_Hangul_AE 0xec0 +#define XK_Hangul_YA 0xec1 +#define XK_Hangul_YAE 0xec2 +#define XK_Hangul_EO 0xec3 +#define XK_Hangul_E 0xec4 +#define XK_Hangul_YEO 0xec5 +#define XK_Hangul_YE 0xec6 +#define XK_Hangul_O 0xec7 +#define XK_Hangul_WA 0xec8 +#define XK_Hangul_WAE 0xec9 +#define XK_Hangul_OE 0xeca +#define XK_Hangul_YO 0xecb +#define XK_Hangul_U 0xecc +#define XK_Hangul_WEO 0xecd +#define XK_Hangul_WE 0xece +#define XK_Hangul_WI 0xecf +#define XK_Hangul_YU 0xed0 +#define XK_Hangul_EU 0xed1 +#define XK_Hangul_YI 0xed2 +#define XK_Hangul_I 0xed3 + +/* Hangul syllable-final (JongSeong) Characters */ +#define XK_Hangul_J_Kiyeog 0xed4 +#define XK_Hangul_J_SsangKiyeog 0xed5 +#define XK_Hangul_J_KiyeogSios 0xed6 +#define XK_Hangul_J_Nieun 0xed7 +#define XK_Hangul_J_NieunJieuj 0xed8 +#define XK_Hangul_J_NieunHieuh 0xed9 +#define XK_Hangul_J_Dikeud 0xeda +#define XK_Hangul_J_Rieul 0xedb +#define XK_Hangul_J_RieulKiyeog 0xedc +#define XK_Hangul_J_RieulMieum 0xedd +#define XK_Hangul_J_RieulPieub 0xede +#define XK_Hangul_J_RieulSios 0xedf +#define XK_Hangul_J_RieulTieut 0xee0 +#define XK_Hangul_J_RieulPhieuf 0xee1 +#define XK_Hangul_J_RieulHieuh 0xee2 +#define XK_Hangul_J_Mieum 0xee3 +#define XK_Hangul_J_Pieub 0xee4 +#define XK_Hangul_J_PieubSios 0xee5 +#define XK_Hangul_J_Sios 0xee6 +#define XK_Hangul_J_SsangSios 0xee7 +#define XK_Hangul_J_Ieung 0xee8 +#define XK_Hangul_J_Jieuj 0xee9 +#define XK_Hangul_J_Cieuc 0xeea +#define XK_Hangul_J_Khieuq 0xeeb +#define XK_Hangul_J_Tieut 0xeec +#define XK_Hangul_J_Phieuf 0xeed +#define XK_Hangul_J_Hieuh 0xeee + +/* Ancient Hangul Consonant Characters */ +#define XK_Hangul_RieulYeorinHieuh 0xeef +#define XK_Hangul_SunkyeongeumMieum 0xef0 +#define XK_Hangul_SunkyeongeumPieub 0xef1 +#define XK_Hangul_PanSios 0xef2 +#define XK_Hangul_KkogjiDalrinIeung 0xef3 +#define XK_Hangul_SunkyeongeumPhieuf 0xef4 +#define XK_Hangul_YeorinHieuh 0xef5 + +/* Ancient Hangul Vowel Characters */ +#define XK_Hangul_AraeA 0xef6 +#define XK_Hangul_AraeAE 0xef7 + +/* Ancient Hangul syllable-final (JongSeong) Characters */ +#define XK_Hangul_J_PanSios 0xef8 +#define XK_Hangul_J_KkogjiDalrinIeung 0xef9 +#define XK_Hangul_J_YeorinHieuh 0xefa + +/* Korean currency symbol */ +#define XK_Korean_Won 0xeff + +#endif /* XK_KOREAN */ + +#ifdef XK_CURRENCY +#define XK_EcuSign 0x20a0 +#define XK_ColonSign 0x20a1 +#define XK_CruzeiroSign 0x20a2 +#define XK_FFrancSign 0x20a3 +#define XK_LiraSign 0x20a4 +#define XK_MillSign 0x20a5 +#define XK_NairaSign 0x20a6 +#define XK_PesetaSign 0x20a7 +#define XK_RupeeSign 0x20a8 +#define XK_WonSign 0x20a9 +#define XK_NewSheqelSign 0x20aa +#define XK_DongSign 0x20ab +#define XK_EuroSign 0x20ac +#endif diff --git a/common/rfb/msgTypes.h b/common/rfb/msgTypes.h new file mode 100644 index 00000000..f6f8d5cf --- /dev/null +++ b/common/rfb/msgTypes.h @@ -0,0 +1,57 @@ +/* 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_MSGTYPES_H__ +#define __RFB_MSGTYPES_H__ + +namespace rfb { + // server to client + + const int msgTypeFramebufferUpdate = 0; + const int msgTypeSetColourMapEntries = 1; + const int msgTypeBell = 2; + const int msgTypeServerCutText = 3; + + const int msgTypeFileListData = 130; + const int msgTypeFileDownloadData = 131; + const int msgTypeFileUploadCancel = 132; + const int msgTypeFileDownloadFailed = 133; + const int msgTypeFileDirSizeData = 134; + const int msgTypeFileLastRequestFailed = 135; + + // client to server + + const int msgTypeSetPixelFormat = 0; + const int msgTypeFixColourMapEntries = 1; + const int msgTypeSetEncodings = 2; + const int msgTypeFramebufferUpdateRequest = 3; + const int msgTypeKeyEvent = 4; + const int msgTypePointerEvent = 5; + const int msgTypeClientCutText = 6; + + const int msgTypeFileListRequest = 130; + const int msgTypeFileDownloadRequest = 131; + const int msgTypeFileUploadRequest = 132; + const int msgTypeFileUploadData = 133; + const int msgTypeFileDownloadCancel = 134; + const int msgTypeFileUploadFailed = 135; + const int msgTypeFileCreateDirRequest = 136; + const int msgTypeFileDirSizeRequest = 137; + const int msgTypeFileRenameRequest = 138; + const int msgTypeFileDeleteRequest = 139; +} +#endif diff --git a/common/rfb/rfb.dsp b/common/rfb/rfb.dsp new file mode 100644 index 00000000..aef5e43c --- /dev/null +++ b/common/rfb/rfb.dsp @@ -0,0 +1,767 @@ +# Microsoft Developer Studio Project File - Name="rfb" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=rfb - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "rfb.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "rfb.mak" CFG="rfb - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "rfb - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "rfb - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE "rfb - Win32 Debug Unicode" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "rfb - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\Release"
+# PROP Intermediate_Dir "..\Release\rfb"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O1 /I ".." /I "../../win" /FI"rdr/msvcwarning.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "rfb - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug"
+# PROP Intermediate_Dir "..\Debug\rfb"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /I "../../win" /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "rfb - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "rfb___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "rfb___Win32_Debug_Unicode"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug_Unicode"
+# PROP Intermediate_Dir "..\Debug_Unicode\rfb"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /I "../../win" /FI"rdr/msvcwarning.h" /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF
+
+# Begin Target
+
+# Name "rfb - Win32 Release"
+# Name "rfb - Win32 Debug"
+# Name "rfb - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\Blacklist.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CConnection.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CFTMsgReader.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CFTMsgWriter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgHandler.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgReader.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgReaderV3.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgWriter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgWriterV3.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComparingUpdateTracker.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Configuration.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConnParams.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CSecurityVncAuth.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Cursor.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\d3des.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\Decoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Encoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\encodings.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\FileInfo.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\FileManager.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\FileReader.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\FileWriter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\HextileDecoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\HextileEncoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\HTTPServer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\KeyRemapper.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger_file.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger_stdio.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\LogWriter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Password.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelBuffer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelFormat.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RawDecoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RawEncoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Region.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RREDecoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RREEncoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ScaledPixelBuffer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SConnection.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\secTypes.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ServerCore.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SFileTransfer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SFileTransferManager.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SFTMsgReader.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SFTMsgWriter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgHandler.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgReader.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgReaderV3.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgWriter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgWriterV3.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityFactoryStandard.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityVncAuth.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\TightDecoder.cxx
+# ADD CPP /I "../jpeg"
+# End Source File
+# Begin Source File
+
+SOURCE=.\TightEncoder.cxx
+# ADD CPP /I "../jpeg"
+# End Source File
+# Begin Source File
+
+SOURCE=.\TightPalette.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\TransferQueue.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\TransImageGetter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\UpdateTracker.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\util.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCSConnectionST.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServerST.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZRLEDecoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZRLEEncoder.cxx
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\Blacklist.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CConnection.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CFTMsgReader.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CFTMsgWriter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgHandler.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgReader.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgReaderV3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgWriter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgWriterV3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ColourCube.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ColourMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComparingUpdateTracker.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Configuration.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConnParams.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CSecurity.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CSecurityNone.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CSecurityVncAuth.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Cursor.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\d3des.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Decoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DirManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Encoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\encodings.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Exception.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FileInfo.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FileManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FileReader.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FileWriter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hextileConstants.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hextileDecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HextileDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hextileEncode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hextileEncodeBetter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HextileEncoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Hostname.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HTTPServer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ImageGetter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\InputHandler.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\KeyRemapper.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\keysymdef.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ListConnInfo.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger_file.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger_stdio.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LogWriter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\msgTypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\msvcwarning.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Password.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Pixel.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelFormat.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RawDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RawEncoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Rect.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Region.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\rreDecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RREDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\rreEncode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RREEncoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ScaledPixelBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SConnection.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDesktop.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\secTypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ServerCore.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SFileTransfer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SFileTransferManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SFTMsgReader.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SFTMsgWriter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgHandler.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgReader.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgReaderV3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgWriter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgWriterV3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurity.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityFactoryStandard.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityNone.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityVncAuth.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Threading.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\tightDecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TightDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\tightEncode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TightEncoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TightPalette.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TransferQueue.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TransImageGetter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\transInitTempl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\transTempl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TrueColourMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\UpdateTracker.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\UserPasswdGetter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\util.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCSConnectionST.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServerST.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zrleDecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZRLEDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zrleEncode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZRLEEncoder.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/common/rfb/rreDecode.h b/common/rfb/rreDecode.h new file mode 100644 index 00000000..1f5bdf8b --- /dev/null +++ b/common/rfb/rreDecode.h @@ -0,0 +1,64 @@ +/* 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. + */ +// +// RRE decoding function. +// +// This file is #included after having set the following macros: +// BPP - 8, 16 or 32 +// EXTRA_ARGS - optional extra arguments +// FILL_RECT - fill a rectangle with a single colour + +#include <rdr/InStream.h> + +namespace rfb { + +// CONCAT2E concatenates its arguments, expanding them if they are macros + +#ifndef CONCAT2E +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) +#endif + +#define PIXEL_T rdr::CONCAT2E(U,BPP) +#define READ_PIXEL CONCAT2E(readOpaque,BPP) +#define RRE_DECODE CONCAT2E(rreDecode,BPP) + +void RRE_DECODE (const Rect& r, rdr::InStream* is +#ifdef EXTRA_ARGS + , EXTRA_ARGS +#endif + ) +{ + int nSubrects = is->readU32(); + PIXEL_T bg = is->READ_PIXEL(); + FILL_RECT(r, bg); + + for (int i = 0; i < nSubrects; i++) { + PIXEL_T pix = is->READ_PIXEL(); + int x = is->readU16(); + int y = is->readU16(); + int w = is->readU16(); + int h = is->readU16(); + FILL_RECT(Rect(r.tl.x+x, r.tl.y+y, r.tl.x+x+w, r.tl.y+y+h), pix); + } +} + +#undef PIXEL_T +#undef READ_PIXEL +#undef RRE_DECODE +} diff --git a/common/rfb/rreEncode.h b/common/rfb/rreEncode.h new file mode 100644 index 00000000..3f834877 --- /dev/null +++ b/common/rfb/rreEncode.h @@ -0,0 +1,164 @@ +/* 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. + */ +// +// RRE encoding function. +// +// This file is #included after having set the following macros: +// BPP - 8, 16 or 32 +// +// The data argument to RRE_ENCODE contains the pixel data, and it writes the +// encoded version to the given OutStream. If the encoded version exceeds w*h +// it aborts and returns -1, otherwise it returns the number of subrectangles. +// + +#include <rdr/OutStream.h> + +namespace rfb { + +// CONCAT2E concatenates its arguments, expanding them if they are macros + +#ifndef CONCAT2E +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) +#endif + +#define PIXEL_T rdr::CONCAT2E(U,BPP) +#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP) +#define RRE_ENCODE CONCAT2E(rreEncode,BPP) + +int RRE_ENCODE (PIXEL_T* data, int w, int h, rdr::OutStream* os, PIXEL_T bg); + +int RRE_ENCODE (void* data, int w, int h, rdr::OutStream* os) +{ + // Find the background colour - count occurrences of up to 4 different pixel + // values, and choose the one which occurs most often. + + const int nCols = 4; + PIXEL_T pix[nCols]; + int count[nCols] = { 0, }; + PIXEL_T* ptr = (PIXEL_T*)data; + PIXEL_T* end = ptr + w*h; + + while (ptr < end) { + int i; + for (i = 0; i < nCols; i++) { + if (count[i] == 0) + pix[i] = *ptr; + + if (pix[i] == *ptr) { + count[i]++; + break; + } + } + + if (i == nCols) break; + ptr++; + } + + int bg = 0; + for (int i = 1; i < nCols; i++) + if (count[i] > count[bg]) bg = i; + + // Now call the function to do the encoding. + + return RRE_ENCODE ((PIXEL_T*)data, w, h, os, pix[bg]); +} + +int RRE_ENCODE (PIXEL_T* data, int w, int h, rdr::OutStream* os, PIXEL_T bg) +{ + int oldLen = os->length(); + os->WRITE_PIXEL(bg); + + int nSubrects = 0; + + for (int y = 0; y < h; y++) + { + int x = 0; + while (x < w) { + if (*data == bg) { + x++; + data++; + continue; + } + + // Find horizontal subrect first + PIXEL_T* ptr = data+1; + PIXEL_T* eol = data+w-x; + while (ptr < eol && *ptr == *data) ptr++; + int sw = ptr - data; + + ptr = data + w; + int sh = 1; + while (sh < h-y) { + eol = ptr + sw; + while (ptr < eol) + if (*ptr++ != *data) goto endOfHorizSubrect; + ptr += w - sw; + sh++; + } + endOfHorizSubrect: + + // Find vertical subrect + int vh; + for (vh = sh; vh < h-y; vh++) + if (data[vh*w] != *data) break; + + if (vh != sh) { + ptr = data+1; + int vw; + for (vw = 1; vw < sw; vw++) { + for (int i = 0; i < vh; i++) + if (ptr[i*w] != *data) goto endOfVertSubrect; + ptr++; + } + endOfVertSubrect: + + // If vertical subrect bigger than horizontal then use that. + if (sw*sh < vw*vh) { + sw = vw; + sh = vh; + } + } + + nSubrects++; + os->WRITE_PIXEL(*data); + os->writeU16(x); + os->writeU16(y); + os->writeU16(sw); + os->writeU16(sh); + if (os->length() > oldLen + w*h) return -1; + + ptr = data+w; + PIXEL_T* eor = data+w*sh; + while (ptr < eor) { + eol = ptr + sw; + while (ptr < eol) *ptr++ = bg; + ptr += w - sw; + } + x += sw; + data += sw; + } + } + + return nSubrects; +} + +#undef PIXEL_T +#undef WRITE_PIXEL +#undef RRE_ENCODE +} diff --git a/common/rfb/secTypes.cxx b/common/rfb/secTypes.cxx new file mode 100644 index 00000000..830d8444 --- /dev/null +++ b/common/rfb/secTypes.cxx @@ -0,0 +1,73 @@ +/* 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 <string.h> +#ifdef _WIN32 +#define strcasecmp _stricmp +#endif +#include <rfb/secTypes.h> +#include <rfb/util.h> + +int rfb::secTypeNum(const char* name) +{ + if (strcasecmp(name, "None") == 0) return secTypeNone; + if (strcasecmp(name, "VncAuth") == 0) return secTypeVncAuth; + if (strcasecmp(name, "Tight") == 0) return secTypeTight; + if (strcasecmp(name, "RA2") == 0) return secTypeRA2; + if (strcasecmp(name, "RA2ne") == 0) return secTypeRA2ne; + if (strcasecmp(name, "SSPI") == 0) return secTypeSSPI; + if (strcasecmp(name, "SSPIne") == 0) return secTypeSSPIne; + return secTypeInvalid; +} + +const char* rfb::secTypeName(int num) +{ + switch (num) { + case secTypeNone: return "None"; + case secTypeVncAuth: return "VncAuth"; + case secTypeTight: return "Tight"; + case secTypeRA2: return "RA2"; + case secTypeRA2ne: return "RA2ne"; + case secTypeSSPI: return "SSPI"; + case secTypeSSPIne: return "SSPIne"; + default: return "[unknown secType]"; + } +} + +bool rfb::secTypeEncrypts(int num) +{ + switch (num) { + case secTypeRA2: + case secTypeSSPI: + return true; + default: + return false; + } +} + +std::list<int> rfb::parseSecTypes(const char* types_) +{ + std::list<int> result; + CharArray types(strDup(types_)), type; + while (types.buf) { + strSplit(types.buf, ',', &type.buf, &types.buf); + int typeNum = secTypeNum(type.buf); + if (typeNum != secTypeInvalid) + result.push_back(typeNum); + } + return result; +} diff --git a/common/rfb/secTypes.h b/common/rfb/secTypes.h new file mode 100644 index 00000000..3cf783bd --- /dev/null +++ b/common/rfb/secTypes.h @@ -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. + */ +// +// secTypes.h - constants for the various security types. +// + +#ifndef __RFB_SECTYPES_H__ +#define __RFB_SECTYPES_H__ + +#include <list> + +namespace rfb { + const int secTypeInvalid = 0; + const int secTypeNone = 1; + const int secTypeVncAuth = 2; + + const int secTypeRA2 = 5; + const int secTypeRA2ne = 6; + + const int secTypeSSPI = 7; + const int secTypeSSPIne = 8; + + const int secTypeTight = 16; + const int secTypeUltra = 17; + const int secTypeTLS = 18; + + // result types + + const int secResultOK = 0; + const int secResultFailed = 1; + const int secResultTooMany = 2; // deprecated + + const char* secTypeName(int num); + int secTypeNum(const char* name); + bool secTypeEncrypts(int num); + std::list<int> parseSecTypes(const char* types); +} + +#endif diff --git a/common/rfb/tightDecode.h b/common/rfb/tightDecode.h new file mode 100644 index 00000000..dd0c4d97 --- /dev/null +++ b/common/rfb/tightDecode.h @@ -0,0 +1,412 @@ +/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved. + * Copyright (C) 2004-2005 Cendio AB. 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. + */ + +// +// Tight decoding functions. +// +// This file is #included after having set the following macros: +// BPP - 8, 16 or 32 +// EXTRA_ARGS - optional extra arguments +// FILL_RECT - fill a rectangle with a single color +// IMAGE_RECT - draw a rectangle of pixel data from a buffer + +#include <rdr/InStream.h> +#include <rdr/ZlibInStream.h> +#include <rfb/Exception.h> +#include <assert.h> + +namespace rfb { + +// CONCAT2E concatenates its arguments, expanding them if they are macros + +#ifndef CONCAT2E +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) +#endif + +#define PIXEL_T rdr::CONCAT2E(U,BPP) +#define READ_PIXEL CONCAT2E(readOpaque,BPP) +#define TIGHT_DECODE CONCAT2E(tightDecode,BPP) + +#define TIGHT_MIN_TO_COMPRESS 12 +static bool DecompressJpegRect(const Rect& r, rdr::InStream* is, + PIXEL_T* buf, CMsgHandler* handler); +static void FilterGradient(const Rect& r, rdr::InStream* is, int dataSize, + PIXEL_T* buf, CMsgHandler* handler); +#if BPP == 32 +static void FilterGradient24(const Rect& r, rdr::InStream* is, int dataSize, + PIXEL_T* buf, CMsgHandler* handler); +#endif + +// Main function implementing Tight decoder + +void TIGHT_DECODE (const Rect& r, rdr::InStream* is, + rdr::ZlibInStream zis[], PIXEL_T* buf +#ifdef EXTRA_ARGS + , EXTRA_ARGS +#endif + ) +{ + rdr::U8 *bytebuf = (rdr::U8*) buf; + bool cutZeros = false; + const rfb::PixelFormat& myFormat = handler->cp.pf(); +#if BPP == 32 + if (myFormat.depth == 24 && myFormat.redMax == 0xFF && + myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) { + cutZeros = true; + } +#endif + + rdr::U8 comp_ctl = is->readU8(); + + // Flush zlib streams if we are told by the server to do so. + for (int i = 0; i < 4; i++) { + if (comp_ctl & 1) { + zis[i].reset(); + } + comp_ctl >>= 1; + } + + // "Fill" compression type. + if (comp_ctl == rfbTightFill) { + PIXEL_T pix; + if (cutZeros) { + is->readBytes(bytebuf, 3); + pix = RGB24_TO_PIXEL32(bytebuf[0], bytebuf[1], bytebuf[2]); + } else { + pix = is->READ_PIXEL(); + } + FILL_RECT(r, pix); + return; + } + + // "JPEG" compression type. + if (comp_ctl == rfbTightJpeg) { + DecompressJpegRect(r, is, buf, handler); + return; + } + + // Quit on unsupported compression type. + if (comp_ctl > rfbTightMaxSubencoding) { + throw Exception("TightDecoder: bad subencoding value received"); + return; + } + + // "Basic" compression type. + int palSize = 0; + static PIXEL_T palette[256]; + bool useGradient = false; + + if ((comp_ctl & rfbTightExplicitFilter) != 0) { + rdr::U8 filterId = is->readU8(); + + switch (filterId) { + case rfbTightFilterPalette: + palSize = is->readU8() + 1; + if (cutZeros) { + rdr::U8 *tightPalette = (rdr::U8*) palette; + is->readBytes(tightPalette, palSize*3); + for (int i = palSize - 1; i >= 0; i--) { + palette[i] = RGB24_TO_PIXEL32(tightPalette[i*3], + tightPalette[i*3+1], + tightPalette[i*3+2]); + } + } else { + for (int i = 0; i < palSize; i++) + palette[i] = is->READ_PIXEL(); + } + break; + case rfbTightFilterGradient: + useGradient = true; + break; + case rfbTightFilterCopy: + break; + default: + throw Exception("TightDecoder: unknown filter code received"); + return; + } + } + + int bppp = BPP; + if (palSize != 0) { + bppp = (palSize <= 2) ? 1 : 8; + } else if (cutZeros) { + bppp = 24; + } + + // Determine if the data should be decompressed or just copied. + int rowSize = (r.width() * bppp + 7) / 8; + int dataSize = r.height() * rowSize; + int streamId = -1; + rdr::InStream *input; + if (dataSize < TIGHT_MIN_TO_COMPRESS) { + input = is; + } else { + int length = is->readCompactLength(); + streamId = comp_ctl & 0x03; + zis[streamId].setUnderlying(is, length); + input = &zis[streamId]; + } + + if (palSize == 0) { + // Truecolor data + if (useGradient) { +#if BPP == 32 + if (cutZeros) { + FilterGradient24(r, input, dataSize, buf, handler); + } else +#endif + { + FilterGradient(r, input, dataSize, buf, handler); + } + } else { + input->readBytes(buf, dataSize); + if (cutZeros) { + for (int p = r.height() * r.width() - 1; p >= 0; p--) { + buf[p] = RGB24_TO_PIXEL32(bytebuf[p*3], + bytebuf[p*3+1], + bytebuf[p*3+2]); + } + } + } + } else { + int x, y, b, w; + PIXEL_T *ptr = buf; + rdr::U8 bits; + if (palSize <= 2) { + // 2-color palette + w = (r.width() + 7) / 8; + for (y = 0; y < r.height(); y++) { + for (x = 0; x < r.width() / 8; x++) { + bits = input->readU8(); + for (b = 7; b >= 0; b--) { + *ptr++ = palette[bits >> b & 1]; + } + } + if (r.width() % 8 != 0) { + bits = input->readU8(); + for (b = 7; b >= 8 - r.width() % 8; b--) { + *ptr++ = palette[bits >> b & 1]; + } + } + } + } else { + // 256-color palette + for (y = 0; y < r.height(); y++) { + for (x = 0; x < r.width(); x++) { + *ptr++ = palette[input->readU8()]; + } + } + } + } + + IMAGE_RECT(r, buf); + + if (streamId != -1) { + zis[streamId].reset(); + } +} + +static bool +DecompressJpegRect(const Rect& r, rdr::InStream* is, + PIXEL_T* buf, CMsgHandler* handler) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + PIXEL_T *pixelPtr = buf; + static rdr::U8 scanline_buffer[TIGHT_MAX_WIDTH*3]; + JSAMPROW scanline = scanline_buffer; + + // Read length + int compressedLen = is->readCompactLength(); + if (compressedLen <= 0) { + throw Exception("Incorrect data received from the server.\n"); + } + + // Allocate netbuf and read in data + rdr::U8* netbuf = new rdr::U8[compressedLen]; + if (!netbuf) { + throw Exception("rfb::tightDecode unable to allocate buffer"); + } + is->readBytes(netbuf, compressedLen); + + // Set up JPEG decompression + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + JpegSetSrcManager(&cinfo, (char*)netbuf, compressedLen); + jpeg_read_header(&cinfo, TRUE); + cinfo.out_color_space = JCS_RGB; + + jpeg_start_decompress(&cinfo); + if (cinfo.output_width != (unsigned)r.width() || cinfo.output_height != (unsigned)r.height() || + cinfo.output_components != 3) { + jpeg_destroy_decompress(&cinfo); + throw Exception("Tight Encoding: Wrong JPEG data received.\n"); + } + + // Decompress + const rfb::PixelFormat& myFormat = handler->cp.pf(); + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, &scanline, 1); + if (jpegError) { + break; + } + + for (int dx = 0; dx < r.width(); dx++) { + *pixelPtr++ = + RGB24_TO_PIXEL(scanline[dx*3], scanline[dx*3+1], scanline[dx*3+2]); + } + } + + IMAGE_RECT(r, buf); + + if (!jpegError) { + jpeg_finish_decompress(&cinfo); + } + + jpeg_destroy_decompress(&cinfo); + + delete [] netbuf; + + return !jpegError; +} + +#if BPP == 32 + +static void +FilterGradient24(const Rect& r, rdr::InStream* is, int dataSize, + PIXEL_T* buf, CMsgHandler* handler) +{ + int x, y, c; + static rdr::U8 prevRow[TIGHT_MAX_WIDTH*3]; + static rdr::U8 thisRow[TIGHT_MAX_WIDTH*3]; + rdr::U8 pix[3]; + int est[3]; + + memset(prevRow, 0, sizeof(prevRow)); + + // Allocate netbuf and read in data + rdr::U8 *netbuf = new rdr::U8[dataSize]; + if (!netbuf) { + throw Exception("rfb::tightDecode unable to allocate buffer"); + } + is->readBytes(netbuf, dataSize); + + // Set up shortcut variables + const rfb::PixelFormat& myFormat = handler->cp.pf(); + int rectHeight = r.height(); + int rectWidth = r.width(); + + for (y = 0; y < rectHeight; y++) { + /* First pixel in a row */ + for (c = 0; c < 3; c++) { + pix[c] = netbuf[y*rectWidth*3+c] + prevRow[c]; + thisRow[c] = pix[c]; + } + buf[y*rectWidth] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]); + + /* Remaining pixels of a row */ + for (x = 1; x < rectWidth; x++) { + for (c = 0; c < 3; c++) { + est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c]; + if (est[c] > 0xff) { + est[c] = 0xff; + } else if (est[c] < 0) { + est[c] = 0; + } + pix[c] = netbuf[(y*rectWidth+x)*3+c] + est[c]; + thisRow[x*3+c] = pix[c]; + } + buf[y*rectWidth+x] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]); + } + + memcpy(prevRow, thisRow, sizeof(prevRow)); + } + + delete [] netbuf; +} + +#endif + +static void +FilterGradient(const Rect& r, rdr::InStream* is, int dataSize, + PIXEL_T* buf, CMsgHandler* handler) +{ + int x, y, c; + static rdr::U8 prevRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)]; + static rdr::U8 thisRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)]; + int pix[3]; + int max[3]; + int shift[3]; + int est[3]; + + memset(prevRow, 0, sizeof(prevRow)); + + // Allocate netbuf and read in data + PIXEL_T *netbuf = (PIXEL_T*)new rdr::U8[dataSize]; + if (!netbuf) { + throw Exception("rfb::tightDecode unable to allocate buffer"); + } + is->readBytes(netbuf, dataSize); + + // Set up shortcut variables + const rfb::PixelFormat& myFormat = handler->cp.pf(); + max[0] = myFormat.redMax; + max[1] = myFormat.greenMax; + max[2] = myFormat.blueMax; + shift[0] = myFormat.redShift; + shift[1] = myFormat.greenShift; + shift[2] = myFormat.blueShift; + int rectHeight = r.height(); + int rectWidth = r.width(); + + for (y = 0; y < rectHeight; y++) { + /* First pixel in a row */ + for (c = 0; c < 3; c++) { + pix[c] = (netbuf[y*rectWidth] >> shift[c]) + prevRow[c] & max[c]; + thisRow[c] = pix[c]; + } + buf[y*rectWidth] = RGB_TO_PIXEL(pix[0], pix[1], pix[2]); + + /* Remaining pixels of a row */ + for (x = 1; x < rectWidth; x++) { + for (c = 0; c < 3; c++) { + est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c]; + if (est[c] > max[c]) { + est[c] = max[c]; + } else if (est[c] < 0) { + est[c] = 0; + } + pix[c] = (netbuf[y*rectWidth+x] >> shift[c]) + est[c] & max[c]; + thisRow[x*3+c] = pix[c]; + } + buf[y*rectWidth+x] = RGB_TO_PIXEL(pix[0], pix[1], pix[2]); + } + + memcpy(prevRow, thisRow, sizeof(prevRow)); + } + + delete [] netbuf; +} + +#undef TIGHT_MIN_TO_COMPRESS +#undef TIGHT_DECODE +#undef READ_PIXEL +#undef PIXEL_T +} diff --git a/common/rfb/tightEncode.h b/common/rfb/tightEncode.h new file mode 100644 index 00000000..71f076e6 --- /dev/null +++ b/common/rfb/tightEncode.h @@ -0,0 +1,720 @@ +/* Copyright (C) 2000-2003 Constantin Kaplinsky. 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. + */ + +// +// tightEncode.h - Tight encoding function. +// +// This file is #included after having set the following macros: +// BPP - 8, 16 or 32 +// EXTRA_ARGS - optional extra arguments +// GET_IMAGE_INTO_BUF - gets a rectangle of pixel data into a buffer +// + +#include <rdr/OutStream.h> +#include <rdr/ZlibOutStream.h> +#include <assert.h> + +namespace rfb { + +// CONCAT2E concatenates its arguments, expanding them if they are macros + +#ifndef CONCAT2E +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) +#endif + +#define PIXEL_T rdr::CONCAT2E(U,BPP) +#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP) +#define TIGHT_ENCODE CONCAT2E(tightEncode,BPP) +#define SWAP_PIXEL CONCAT2E(SWAP,BPP) +#define HASH_FUNCTION CONCAT2E(HASH_FUNC,BPP) +#define PACK_PIXELS CONCAT2E(packPixels,BPP) +#define DETECT_SMOOTH_IMAGE CONCAT2E(detectSmoothImage,BPP) +#define ENCODE_SOLID_RECT CONCAT2E(encodeSolidRect,BPP) +#define ENCODE_FULLCOLOR_RECT CONCAT2E(encodeFullColorRect,BPP) +#define ENCODE_MONO_RECT CONCAT2E(encodeMonoRect,BPP) +#define ENCODE_INDEXED_RECT CONCAT2E(encodeIndexedRect,BPP) +#define PREPARE_JPEG_ROW CONCAT2E(prepareJpegRow,BPP) +#define ENCODE_JPEG_RECT CONCAT2E(encodeJpegRect,BPP) +#define FILL_PALETTE CONCAT2E(fillPalette,BPP) + +#ifndef TIGHT_ONCE +#define TIGHT_ONCE + +// +// C-style structures to store palette entries and compression paramentes. +// Such code probably should be converted into C++ classes. +// + +struct TIGHT_COLOR_LIST { + TIGHT_COLOR_LIST *next; + int idx; + rdr::U32 rgb; +}; + +struct TIGHT_PALETTE_ENTRY { + TIGHT_COLOR_LIST *listNode; + int numPixels; +}; + +struct TIGHT_PALETTE { + TIGHT_PALETTE_ENTRY entry[256]; + TIGHT_COLOR_LIST *hash[256]; + TIGHT_COLOR_LIST list[256]; +}; + +// FIXME: Is it really a good idea to use static variables for this? +static int s_endianMismatch; // local/remote formats differ in byte order +static bool s_pack24; // use 24-bit packing for 32-bit pixels +static int s_rs, s_gs, s_bs; // shifts for 24-bit pixel conversion + +// FIXME: Make a separate class for palette operations. +static int s_palMaxColors, s_palNumColors; +static rdr::U32 s_monoBackground, s_monoForeground; +static TIGHT_PALETTE s_palette; + +// +// Swapping bytes in pixels. +// FIXME: Use a sort of ImageGetter that does not convert pixel format? +// + +#ifndef SWAP16 +#define SWAP16(n) ((((n) & 0xff) << 8) | (((n) >> 8) & 0xff)) +#endif +#ifndef SWAP32 +#define SWAP32(n) (((n) >> 24) | (((n) & 0x00ff0000) >> 8) | \ + (((n) & 0x0000ff00) << 8) | ((n) << 24)) +#endif + +// +// Functions to operate on palette structures. +// + +#define HASH_FUNC16(rgb) ((int)(((rgb >> 8) + rgb) & 0xFF)) +#define HASH_FUNC32(rgb) ((int)(((rgb >> 16) + (rgb >> 8)) & 0xFF)) + +static void paletteReset(void) +{ + s_palNumColors = 0; + memset(s_palette.hash, 0, 256 * sizeof(TIGHT_COLOR_LIST *)); +} + +static int paletteInsert(rdr::U32 rgb, int numPixels, int bpp) +{ + TIGHT_COLOR_LIST *pnode; + TIGHT_COLOR_LIST *prev_pnode = NULL; + int hash_key, idx, new_idx, count; + + hash_key = (bpp == 16) ? HASH_FUNC16(rgb) : HASH_FUNC32(rgb); + + pnode = s_palette.hash[hash_key]; + + while (pnode != NULL) { + if (pnode->rgb == rgb) { + // Such palette entry already exists. + new_idx = idx = pnode->idx; + count = s_palette.entry[idx].numPixels + numPixels; + if (new_idx && s_palette.entry[new_idx-1].numPixels < count) { + do { + s_palette.entry[new_idx] = s_palette.entry[new_idx-1]; + s_palette.entry[new_idx].listNode->idx = new_idx; + new_idx--; + } + while (new_idx && + s_palette.entry[new_idx-1].numPixels < count); + s_palette.entry[new_idx].listNode = pnode; + pnode->idx = new_idx; + } + s_palette.entry[new_idx].numPixels = count; + return s_palNumColors; + } + prev_pnode = pnode; + pnode = pnode->next; + } + + // Check if palette is full. + if ( s_palNumColors == 256 || s_palNumColors == s_palMaxColors ) { + s_palNumColors = 0; + return 0; + } + + // Move palette entries with lesser pixel counts. + for ( idx = s_palNumColors; + idx > 0 && s_palette.entry[idx-1].numPixels < numPixels; + idx-- ) { + s_palette.entry[idx] = s_palette.entry[idx-1]; + s_palette.entry[idx].listNode->idx = idx; + } + + // Add new palette entry into the freed slot. + pnode = &s_palette.list[s_palNumColors]; + if (prev_pnode != NULL) { + prev_pnode->next = pnode; + } else { + s_palette.hash[hash_key] = pnode; + } + pnode->next = NULL; + pnode->idx = idx; + pnode->rgb = rgb; + s_palette.entry[idx].listNode = pnode; + s_palette.entry[idx].numPixels = numPixels; + + return (++s_palNumColors); +} + +// +// Compress the data (but do not perform actual compression if the data +// size is less than TIGHT_MIN_TO_COMPRESS bytes. +// + +static void compressData(rdr::OutStream *os, rdr::ZlibOutStream *zos, + const void *buf, unsigned int length, int zlibLevel) +{ + if (length < TIGHT_MIN_TO_COMPRESS) { + os->writeBytes(buf, length); + } else { + // FIXME: Using a temporary MemOutStream may be not efficient. + // Maybe use the same static object used in the JPEG coder? + rdr::MemOutStream mem_os; + zos->setUnderlying(&mem_os); + zos->setCompressionLevel(zlibLevel); + zos->writeBytes(buf, length); + zos->flush(); + os->writeCompactLength(mem_os.length()); + os->writeBytes(mem_os.data(), mem_os.length()); + } +} + +// +// Destination manager implementation for the JPEG library. +// FIXME: Implement JPEG compression in new rdr::JpegOutStream class. +// + +// FIXME: Keeping a MemOutStream instance may consume too much space. +rdr::MemOutStream s_jpeg_os; + +static struct jpeg_destination_mgr s_jpegDstManager; +static JOCTET *s_jpegDstBuffer; +static size_t s_jpegDstBufferLen; + +static void +JpegInitDestination(j_compress_ptr cinfo) +{ + s_jpeg_os.clear(); + s_jpegDstManager.next_output_byte = s_jpegDstBuffer; + s_jpegDstManager.free_in_buffer = s_jpegDstBufferLen; +} + +static boolean +JpegEmptyOutputBuffer(j_compress_ptr cinfo) +{ + s_jpeg_os.writeBytes(s_jpegDstBuffer, s_jpegDstBufferLen); + s_jpegDstManager.next_output_byte = s_jpegDstBuffer; + s_jpegDstManager.free_in_buffer = s_jpegDstBufferLen; + + return TRUE; +} + +static void +JpegTermDestination(j_compress_ptr cinfo) +{ + int dataLen = s_jpegDstBufferLen - s_jpegDstManager.free_in_buffer; + s_jpeg_os.writeBytes(s_jpegDstBuffer, dataLen); +} + +static void +JpegSetDstManager(j_compress_ptr cinfo, JOCTET *buf, size_t buflen) +{ + s_jpegDstBuffer = buf; + s_jpegDstBufferLen = buflen; + s_jpegDstManager.init_destination = JpegInitDestination; + s_jpegDstManager.empty_output_buffer = JpegEmptyOutputBuffer; + s_jpegDstManager.term_destination = JpegTermDestination; + cinfo->dest = &s_jpegDstManager; +} + +#endif // #ifndef TIGHT_ONCE + +static void ENCODE_SOLID_RECT (rdr::OutStream *os, + PIXEL_T *buf); +static void ENCODE_FULLCOLOR_RECT (rdr::OutStream *os, rdr::ZlibOutStream zos[4], + PIXEL_T *buf, const Rect& r); +static void ENCODE_MONO_RECT (rdr::OutStream *os, rdr::ZlibOutStream zos[4], + PIXEL_T *buf, const Rect& r); +#if (BPP != 8) +static void ENCODE_INDEXED_RECT (rdr::OutStream *os, rdr::ZlibOutStream zos[4], + PIXEL_T *buf, const Rect& r); +static void ENCODE_JPEG_RECT (rdr::OutStream *os, + PIXEL_T *buf, const PixelFormat& pf, const Rect& r); +#endif + +static void FILL_PALETTE (PIXEL_T *data, int count); + +// +// Convert 32-bit color samples into 24-bit colors, in place. +// Performs packing only when redMax, greenMax and blueMax are all 255. +// Color components are assumed to be byte-aligned. +// + +static inline unsigned int PACK_PIXELS (PIXEL_T *buf, unsigned int count) +{ +#if (BPP != 32) + return count * sizeof(PIXEL_T); +#else + if (!s_pack24) + return count * sizeof(PIXEL_T); + + rdr::U32 pix; + rdr::U8 *dst = (rdr::U8 *)buf; + for (unsigned int i = 0; i < count; i++) { + pix = *buf++; + *dst++ = (rdr::U8)(pix >> s_rs); + *dst++ = (rdr::U8)(pix >> s_gs); + *dst++ = (rdr::U8)(pix >> s_bs); + } + return count * 3; +#endif +} + +// +// Function to guess if a given rectangle is suitable for JPEG compression. +// Returns true is it looks like a good data for JPEG, false otherwise. +// +// FIXME: Scan the image and determine is it really good for JPEG. +// + +#if (BPP != 8) +static bool DETECT_SMOOTH_IMAGE (PIXEL_T *buf, const Rect& r) +{ + if (r.width() < TIGHT_DETECT_MIN_WIDTH || + r.height() < TIGHT_DETECT_MIN_HEIGHT || + r.area() < TIGHT_JPEG_MIN_RECT_SIZE || + s_pjconf == NULL) + return 0; + + return 1; +} +#endif + +// FIXME: Split rectangles into smaller ones! +// FIXME: Compare encoder code with 1.3 before the final version. + +// +// Main function of the Tight encoder +// + +void TIGHT_ENCODE (const Rect& r, rdr::OutStream *os, + rdr::ZlibOutStream zos[4], void* buf, ConnParams* cp +#ifdef EXTRA_ARGS + , EXTRA_ARGS +#endif + ) +{ + const PixelFormat& pf = cp->pf(); + GET_IMAGE_INTO_BUF(r, buf); + PIXEL_T* pixels = (PIXEL_T*)buf; + +#if (BPP != 8) + union { + rdr::U32 value32; + rdr::U8 test; + } littleEndian; + littleEndian.value32 = 1; + s_endianMismatch = (littleEndian.test != !pf.bigEndian); +#endif + +#if (BPP == 32) + // Check if it's necessary to pack 24-bit pixels, and + // compute appropriate shift values if necessary. + s_pack24 = (pf.depth == 24 && pf.redMax == 0xFF && + pf.greenMax == 0xFF && pf.blueMax == 0xFF); + if (s_pack24) { + if (!s_endianMismatch) { + s_rs = pf.redShift; + s_gs = pf.greenShift; + s_bs = pf.blueShift; + } else { + s_rs = 24 - pf.redShift; + s_gs = 24 - pf.greenShift; + s_bs = 24 - pf.blueShift; + } + } +#endif + + s_palMaxColors = r.area() / s_pconf->idxMaxColorsDivisor; + if (s_palMaxColors < 2 && r.area() >= s_pconf->monoMinRectSize) { + s_palMaxColors = 2; + } + // FIXME: Temporary limitation for switching to JPEG earlier. + if (s_palMaxColors > 96 && s_pjconf != NULL) { + s_palMaxColors = 96; + } + + FILL_PALETTE(pixels, r.area()); + + switch (s_palNumColors) { + case 0: + // Truecolor image +#if (BPP != 8) + if (s_pjconf != NULL && DETECT_SMOOTH_IMAGE(pixels, r)) { + ENCODE_JPEG_RECT(os, pixels, pf, r); + break; + } +#endif + ENCODE_FULLCOLOR_RECT(os, zos, pixels, r); + break; + case 1: + // Solid rectangle + ENCODE_SOLID_RECT(os, pixels); + break; + case 2: + // Two-color rectangle + ENCODE_MONO_RECT(os, zos, pixels, r); + break; +#if (BPP != 8) + default: + // Up to 256 different colors + ENCODE_INDEXED_RECT(os, zos, pixels, r); +#endif + } +} + +// +// Subencoding implementations. +// + +static void ENCODE_SOLID_RECT (rdr::OutStream *os, PIXEL_T *buf) +{ + os->writeU8(0x08 << 4); + + int length = PACK_PIXELS(buf, 1); + os->writeBytes(buf, length); +} + +static void ENCODE_FULLCOLOR_RECT (rdr::OutStream *os, rdr::ZlibOutStream zos[4], + PIXEL_T *buf, const Rect& r) +{ + const int streamId = 0; + os->writeU8(streamId << 4); + + int length = PACK_PIXELS(buf, r.area()); + compressData(os, &zos[streamId], buf, length, s_pconf->rawZlibLevel); +} + +static void ENCODE_MONO_RECT (rdr::OutStream *os, rdr::ZlibOutStream zos[4], + PIXEL_T *buf, const Rect& r) +{ + const int streamId = 1; + os->writeU8((streamId | 0x04) << 4); + os->writeU8(0x01); + + // Write the palette + PIXEL_T pal[2] = { (PIXEL_T)s_monoBackground, (PIXEL_T)s_monoForeground }; + os->writeU8(1); + os->writeBytes(pal, PACK_PIXELS(pal, 2)); + + // Encode the data in-place + PIXEL_T *src = buf; + rdr::U8 *dst = (rdr::U8 *)buf; + int w = r.width(); + int h = r.height(); + PIXEL_T bg; + unsigned int value, mask; + int aligned_width; + int x, y, bg_bits; + + bg = (PIXEL_T) s_monoBackground; + aligned_width = w - w % 8; + + for (y = 0; y < h; y++) { + for (x = 0; x < aligned_width; x += 8) { + for (bg_bits = 0; bg_bits < 8; bg_bits++) { + if (*src++ != bg) + break; + } + if (bg_bits == 8) { + *dst++ = 0; + continue; + } + mask = 0x80 >> bg_bits; + value = mask; + for (bg_bits++; bg_bits < 8; bg_bits++) { + mask >>= 1; + if (*src++ != bg) { + value |= mask; + } + } + *dst++ = (rdr::U8)value; + } + + mask = 0x80; + value = 0; + if (x >= w) + continue; + + for (; x < w; x++) { + if (*src++ != bg) { + value |= mask; + } + mask >>= 1; + } + *dst++ = (rdr::U8)value; + } + + // Write the data + int length = (w + 7) / 8; + length *= h; + compressData(os, &zos[streamId], buf, length, s_pconf->monoZlibLevel); +} + +#if (BPP != 8) +static void ENCODE_INDEXED_RECT (rdr::OutStream *os, rdr::ZlibOutStream zos[4], + PIXEL_T *buf, const Rect& r) +{ + const int streamId = 2; + os->writeU8((streamId | 0x04) << 4); + os->writeU8(0x01); + + // Write the palette + { + PIXEL_T pal[256]; + for (int i = 0; i < s_palNumColors; i++) + pal[i] = (PIXEL_T)s_palette.entry[i].listNode->rgb; + os->writeU8((rdr::U8)(s_palNumColors - 1)); + os->writeBytes(pal, PACK_PIXELS(pal, s_palNumColors)); + } + + // Encode data in-place + PIXEL_T *src = buf; + rdr::U8 *dst = (rdr::U8 *)buf; + int count = r.area(); + PIXEL_T rgb; + TIGHT_COLOR_LIST *pnode; + int rep = 0; + + while (count--) { + rgb = *src++; + while (count && *src == rgb) { + rep++, src++, count--; + } + pnode = s_palette.hash[HASH_FUNCTION(rgb)]; + while (pnode != NULL) { + if ((PIXEL_T)pnode->rgb == rgb) { + *dst++ = (rdr::U8)pnode->idx; + while (rep) { + *dst++ = (rdr::U8)pnode->idx; + rep--; + } + break; + } + pnode = pnode->next; + } + } + + // Write the data + compressData(os, &zos[streamId], buf, r.area(), s_pconf->idxZlibLevel); +} +#endif // #if (BPP != 8) + +// +// JPEG compression. +// + +#if (BPP != 8) +static void PREPARE_JPEG_ROW (PIXEL_T *src, const PixelFormat& pf, + rdr::U8 *dst, int count) +{ + // FIXME: Add a version of this function optimized for 24-bit colors? + PIXEL_T pix; + while (count--) { + pix = *src++; + if (s_endianMismatch) + pix = SWAP_PIXEL(pix); + *dst++ = (rdr::U8)((pix >> pf.redShift & pf.redMax) * 255 / pf.redMax); + *dst++ = (rdr::U8)((pix >> pf.greenShift & pf.greenMax) * 255 / pf.greenMax); + *dst++ = (rdr::U8)((pix >> pf.blueShift & pf.blueMax) * 255 / pf.blueMax); + } +} +#endif // #if (BPP != 8) + +#if (BPP != 8) +static void ENCODE_JPEG_RECT (rdr::OutStream *os, PIXEL_T *buf, + const PixelFormat& pf, const Rect& r) +{ + int w = r.width(); + int h = r.height(); + + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + + // FIXME: Make srcBuf[] and/or dstBuf[] static? + rdr::U8 *srcBuf = new rdr::U8[w * 3]; + JSAMPROW rowPointer[1]; + rowPointer[0] = (JSAMPROW)srcBuf; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + + cinfo.image_width = w; + cinfo.image_height = h; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, s_pjconf->jpegQuality, TRUE); + + rdr::U8 *dstBuf = new rdr::U8[2048]; + JpegSetDstManager(&cinfo, dstBuf, 2048); + + jpeg_start_compress(&cinfo, TRUE); + for (int dy = 0; dy < h; dy++) { + PREPARE_JPEG_ROW(&buf[dy * w], pf, srcBuf, w); + jpeg_write_scanlines(&cinfo, rowPointer, 1); + } + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + + delete[] srcBuf; + delete[] dstBuf; + + os->writeU8(0x09 << 4); + os->writeCompactLength(s_jpeg_os.length()); + os->writeBytes(s_jpeg_os.data(), s_jpeg_os.length()); +} +#endif // #if (BPP != 8) + +// +// Determine the number of colors in the rectangle, and fill in the palette. +// + +#if (BPP == 8) +static void FILL_PALETTE (PIXEL_T *data, int count) +{ + PIXEL_T c0, c1; + int i, n0, n1; + + s_palNumColors = 0; + + c0 = data[0]; + for (i = 1; i < count && data[i] == c0; i++); + if (i == count) { + s_palNumColors = 1; + return; // Solid rectangle + } + + if (s_palMaxColors < 2) + return; + + n0 = i; + c1 = data[i]; + n1 = 0; + for (i++; i < count; i++) { + if (data[i] == c0) { + n0++; + } else if (data[i] == c1) { + n1++; + } else + break; + } + if (i == count) { + if (n0 > n1) { + s_monoBackground = (rdr::U32)c0; + s_monoForeground = (rdr::U32)c1; + } else { + s_monoBackground = (rdr::U32)c1; + s_monoForeground = (rdr::U32)c0; + } + s_palNumColors = 2; // Two colors + } +} +#else // (BPP != 8) +static void FILL_PALETTE (PIXEL_T *data, int count) +{ + PIXEL_T c0, c1, ci = 0; + int i, n0, n1, ni; + + c0 = data[0]; + for (i = 1; i < count && data[i] == c0; i++); + if (i >= count) { + s_palNumColors = 1; // Solid rectangle + return; + } + + if (s_palMaxColors < 2) { + s_palNumColors = 0; // Full-color format preferred + return; + } + + n0 = i; + c1 = data[i]; + n1 = 0; + for (i++; i < count; i++) { + ci = data[i]; + if (ci == c0) { + n0++; + } else if (ci == c1) { + n1++; + } else + break; + } + if (i >= count) { + if (n0 > n1) { + s_monoBackground = (rdr::U32)c0; + s_monoForeground = (rdr::U32)c1; + } else { + s_monoBackground = (rdr::U32)c1; + s_monoForeground = (rdr::U32)c0; + } + s_palNumColors = 2; // Two colors + return; + } + + paletteReset(); + paletteInsert (c0, (rdr::U32)n0, BPP); + paletteInsert (c1, (rdr::U32)n1, BPP); + + ni = 1; + for (i++; i < count; i++) { + if (data[i] == ci) { + ni++; + } else { + if (!paletteInsert (ci, (rdr::U32)ni, BPP)) + return; + ci = data[i]; + ni = 1; + } + } + paletteInsert (ci, (rdr::U32)ni, BPP); +} +#endif // #if (BPP == 8) + +#undef PIXEL_T +#undef WRITE_PIXEL +#undef TIGHT_ENCODE +#undef SWAP_PIXEL +#undef HASH_FUNCTION +#undef PACK_PIXELS +#undef DETECT_SMOOTH_IMAGE +#undef ENCODE_SOLID_RECT +#undef ENCODE_FULLCOLOR_RECT +#undef ENCODE_MONO_RECT +#undef ENCODE_INDEXED_RECT +#undef PREPARE_JPEG_ROW +#undef ENCODE_JPEG_RECT +#undef FILL_PALETTE +} diff --git a/common/rfb/transInitTempl.h b/common/rfb/transInitTempl.h new file mode 100644 index 00000000..464cfdfc --- /dev/null +++ b/common/rfb/transInitTempl.h @@ -0,0 +1,254 @@ +/* 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. + */ +// +// transInitTempl.h - templates for functions to initialise lookup tables for +// the translation functions. +// +// This file is #included after having set the following macros: +// BPPOUT - 8, 16 or 32 + +#if !defined(BPPOUT) +#error "transInitTempl.h: BPPOUT not defined" +#endif + +namespace rfb { + +// CONCAT2E concatenates its arguments, expanding them if they are macros + +#ifndef CONCAT2E +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) +#endif + +#ifndef SWAP16 +#define SWAP16(n) ((((n) & 0xff) << 8) | (((n) >> 8) & 0xff)) +#endif + +#ifndef SWAP32 +#define SWAP32(n) (((n) >> 24) | (((n) & 0x00ff0000) >> 8) | \ + (((n) & 0x0000ff00) << 8) | ((n) << 24)) +#endif + +#define OUTPIXEL rdr::CONCAT2E(U,BPPOUT) +#define SWAPOUT CONCAT2E(SWAP,BPPOUT) +#define initSimpleCMtoTCOUT CONCAT2E(initSimpleCMtoTC,BPPOUT) +#define initSimpleTCtoTCOUT CONCAT2E(initSimpleTCtoTC,BPPOUT) +#define initSimpleCMtoCubeOUT CONCAT2E(initSimpleCMtoCube,BPPOUT) +#define initSimpleTCtoCubeOUT CONCAT2E(initSimpleTCtoCube,BPPOUT) +#define initRGBTCtoTCOUT CONCAT2E(initRGBTCtoTC,BPPOUT) +#define initRGBTCtoCubeOUT CONCAT2E(initRGBTCtoCube,BPPOUT) +#define initOneRGBTableOUT CONCAT2E(initOneRGBTable,BPPOUT) +#define initOneRGBCubeTableOUT CONCAT2E(initOneRGBCubeTable,BPPOUT) + +#ifndef TRANS_INIT_TEMPL_ENDIAN_TEST +#define TRANS_INIT_TEMPL_ENDIAN_TEST + static rdr::U32 endianTest = 1; + static bool nativeBigEndian = *(rdr::U8*)(&endianTest) != 1; +#endif + +void initSimpleCMtoTCOUT (rdr::U8** tablep, const PixelFormat& inPF, + ColourMap* cm, const PixelFormat& outPF) +{ + if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian) + throw Exception("Internal error: inPF is not native endian"); + + int size = 1 << inPF.bpp; + + delete [] *tablep; + *tablep = new rdr::U8[size * sizeof(OUTPIXEL)]; + OUTPIXEL* table = (OUTPIXEL*)*tablep; + + for (int i = 0; i < size; i++) { + int r,g,b; + cm->lookup(i,&r,&g,&b); + + table[i] = ((((r * outPF.redMax + 32767) / 65535) << outPF.redShift) | + (((g * outPF.greenMax + 32767) / 65535) << outPF.greenShift) | + (((b * outPF.blueMax + 32767) / 65535) << outPF.blueShift)); +#if (BPPOUT != 8) + if (outPF.bigEndian != nativeBigEndian) + table[i] = SWAPOUT (table[i]); +#endif + } +} + +void initSimpleTCtoTCOUT (rdr::U8** tablep, const PixelFormat& inPF, + const PixelFormat& outPF) +{ + if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian) + throw Exception("Internal error: inPF is not native endian"); + + int size = 1 << inPF.bpp; + + delete [] *tablep; + *tablep = new rdr::U8[size * sizeof(OUTPIXEL)]; + OUTPIXEL* table = (OUTPIXEL*)*tablep; + + for (int i = 0; i < size; i++) { + int r = (i >> inPF.redShift) & inPF.redMax; + int g = (i >> inPF.greenShift) & inPF.greenMax; + int b = (i >> inPF.blueShift) & inPF.blueMax; + + r = (r * outPF.redMax + inPF.redMax/2) / inPF.redMax; + g = (g * outPF.greenMax + inPF.greenMax/2) / inPF.greenMax; + b = (b * outPF.blueMax + inPF.blueMax/2) / inPF.blueMax; + + table[i] = ((r << outPF.redShift) | + (g << outPF.greenShift) | + (b << outPF.blueShift)); +#if (BPPOUT != 8) + if (outPF.bigEndian != nativeBigEndian) + table[i] = SWAPOUT (table[i]); +#endif + } +} + +void initSimpleCMtoCubeOUT (rdr::U8** tablep, const PixelFormat& inPF, + ColourMap* cm, ColourCube* cube) +{ + if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian) + throw Exception("Internal error: inPF is not native endian"); + + int size = 1 << inPF.bpp; + + delete [] *tablep; + *tablep = new rdr::U8[size * sizeof(OUTPIXEL)]; + OUTPIXEL* table = (OUTPIXEL*)*tablep; + + for (int i = 0; i < size; i++) { + int r,g,b; + cm->lookup(i,&r,&g,&b); + r = (r * (cube->nRed-1) + 32767) / 65535; + g = (g * (cube->nGreen-1) + 32767) / 65535; + b = (b * (cube->nBlue-1) + 32767) / 65535; + table[i] = cube->lookup(r, g, b); + } +} + +void initSimpleTCtoCubeOUT (rdr::U8** tablep, const PixelFormat& inPF, + ColourCube* cube) +{ + if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian) + throw Exception("Internal error: inPF is not native endian"); + + int size = 1 << inPF.bpp; + + delete [] *tablep; + *tablep = new rdr::U8[size * sizeof(OUTPIXEL)]; + OUTPIXEL* table = (OUTPIXEL*)*tablep; + + for (int i = 0; i < size; i++) { + int r = (i >> inPF.redShift) & inPF.redMax; + int g = (i >> inPF.greenShift) & inPF.greenMax; + int b = (i >> inPF.blueShift) & inPF.blueMax; + + r = (r * (cube->nRed-1) + inPF.redMax/2) / inPF.redMax; + g = (g * (cube->nGreen-1) + inPF.greenMax/2) / inPF.greenMax; + b = (b * (cube->nBlue-1) + inPF.blueMax/2) / inPF.blueMax; + + table[i] = cube->lookup(r, g, b); + } +} + +void initOneRGBTableOUT (OUTPIXEL* table, int inMax, int outMax, + int outShift, bool swap) +{ + int size = inMax + 1; + + for (int i = 0; i < size; i++) { + table[i] = ((i * outMax + inMax / 2) / inMax) << outShift; +#if (BPPOUT != 8) + if (swap) + table[i] = SWAPOUT (table[i]); +#endif + } +} + +void initRGBTCtoTCOUT (rdr::U8** tablep, const PixelFormat& inPF, + const PixelFormat& outPF) +{ + if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian) + throw Exception("Internal error: inPF is not native endian"); + + int size = inPF.redMax + inPF.greenMax + inPF.blueMax + 3; + + delete [] *tablep; + *tablep = new rdr::U8[size * sizeof(OUTPIXEL)]; + + OUTPIXEL* redTable = (OUTPIXEL*)*tablep; + OUTPIXEL* greenTable = redTable + inPF.redMax + 1; + OUTPIXEL* blueTable = greenTable + inPF.greenMax + 1; + + bool swap = (outPF.bigEndian != nativeBigEndian); + + initOneRGBTableOUT (redTable, inPF.redMax, outPF.redMax, + outPF.redShift, swap); + initOneRGBTableOUT (greenTable, inPF.greenMax, outPF.greenMax, + outPF.greenShift, swap); + initOneRGBTableOUT (blueTable, inPF.blueMax, outPF.blueMax, + outPF.blueShift, swap); +} + + +void initOneRGBCubeTableOUT (OUTPIXEL* table, int inMax, int outMax, + int outMult) +{ + int size = inMax + 1; + + for (int i = 0; i < size; i++) { + table[i] = ((i * outMax + inMax / 2) / inMax) * outMult; + } +} + +void initRGBTCtoCubeOUT (rdr::U8** tablep, const PixelFormat& inPF, + ColourCube* cube) +{ + if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian) + throw Exception("Internal error: inPF is not native endian"); + + int size = inPF.redMax + inPF.greenMax + inPF.blueMax + 3 + cube->size(); + + delete [] *tablep; + *tablep = new rdr::U8[size * sizeof(OUTPIXEL)]; + + OUTPIXEL* redTable = (OUTPIXEL*)*tablep; + OUTPIXEL* greenTable = redTable + inPF.redMax + 1; + OUTPIXEL* blueTable = greenTable + inPF.greenMax + 1; + OUTPIXEL* cubeTable = blueTable + inPF.blueMax + 1; + + initOneRGBCubeTableOUT (redTable, inPF.redMax, cube->nRed-1, + cube->redMult()); + initOneRGBCubeTableOUT (greenTable, inPF.greenMax, cube->nGreen-1, + cube->greenMult()); + initOneRGBCubeTableOUT (blueTable, inPF.blueMax, cube->nBlue-1, + cube->blueMult()); + for (int i = 0; i < cube->size(); i++) { + cubeTable[i] = cube->table[i]; + } +} + +#undef OUTPIXEL +#undef initSimpleCMtoTCOUT +#undef initSimpleTCtoTCOUT +#undef initSimpleCMtoCubeOUT +#undef initSimpleTCtoCubeOUT +#undef initRGBTCtoTCOUT +#undef initRGBTCtoCubeOUT +#undef initOneRGBTableOUT +#undef initOneRGBCubeTableOUT +} diff --git a/common/rfb/transTempl.h b/common/rfb/transTempl.h new file mode 100644 index 00000000..09dc7f95 --- /dev/null +++ b/common/rfb/transTempl.h @@ -0,0 +1,151 @@ +/* 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. + */ +// +// transTempl.h - templates for translation functions. +// +// This file is #included after having set the following macros: +// BPPIN - 8, 16 or 32 +// BPPOUT - 8, 16 or 32 + +#if !defined(BPPIN) || !defined(BPPOUT) +#error "transTempl.h: BPPIN or BPPOUT not defined" +#endif + +// CONCAT2E concatenates its arguments, expanding them if they are macros + +#ifndef CONCAT2E +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) +#endif + +#ifndef CONCAT4E +#define CONCAT4(a,b,c,d) a##b##c##d +#define CONCAT4E(a,b,c,d) CONCAT4(a,b,c,d) +#endif + +#define INPIXEL rdr::CONCAT2E(U,BPPIN) +#define OUTPIXEL rdr::CONCAT2E(U,BPPOUT) +#define transSimpleINtoOUT CONCAT4E(transSimple,BPPIN,to,BPPOUT) +#define transRGBINtoOUT CONCAT4E(transRGB,BPPIN,to,BPPOUT) +#define transRGBCubeINtoOUT CONCAT4E(transRGBCube,BPPIN,to,BPPOUT) + +#if (BPPIN <= 16) + +// transSimpleINtoOUT uses a single table. This can be used for any incoming +// and outgoing pixel formats, as long as the incoming pixel format is not too +// large (for 16bpp, the table needs 64K entries). + +void transSimpleINtoOUT (void* table_, + const PixelFormat& inPF, void* inPtr, int inStride, + const PixelFormat& outPF, void* outPtr, int outStride, + int width, int height) +{ + OUTPIXEL* table = (OUTPIXEL*)table_; + INPIXEL* ip = (INPIXEL*)inPtr; + OUTPIXEL* op = (OUTPIXEL*)outPtr; + int inExtra = inStride - width; + int outExtra = outStride - width; + + while (height > 0) { + OUTPIXEL* opEndOfRow = op + width; + while (op < opEndOfRow) + *op++ = table[*ip++]; + ip += inExtra; + op += outExtra; + height--; + } +} + +#endif + +#if (BPPIN >= 16) + +// transRGBINtoOUT uses three tables, one each for red, green and blue +// components and adds the values to produce the result. This can be used +// where a single table would be too large (e.g. 32bpp). It only works for a +// trueColour incoming pixel format. Usually the outgoing pixel format is +// trueColour, but we add rather than ORing the three values so that it is also +// possible to generate an index into a colour cube. I believe that in most +// cases adding is just as fast as ORing - if not then we should split this +// into two different functions for efficiency. + +void transRGBINtoOUT (void* table, + const PixelFormat& inPF, void* inPtr, int inStride, + const PixelFormat& outPF, void* outPtr, int outStride, + int width, int height) +{ + OUTPIXEL* redTable = (OUTPIXEL*)table; + OUTPIXEL* greenTable = redTable + inPF.redMax + 1; + OUTPIXEL* blueTable = greenTable + inPF.greenMax + 1; + INPIXEL* ip = (INPIXEL*)inPtr; + OUTPIXEL* op = (OUTPIXEL*)outPtr; + int inExtra = inStride - width; + int outExtra = outStride - width; + + while (height > 0) { + OUTPIXEL* opEndOfRow = op + width; + while (op < opEndOfRow) { + *op++ = (redTable [(*ip >> inPF.redShift) & inPF.redMax] + + greenTable[(*ip >> inPF.greenShift) & inPF.greenMax] + + blueTable [(*ip >> inPF.blueShift) & inPF.blueMax]); + ip++; + } + ip += inExtra; + op += outExtra; + height--; + } +} + +// transRGBCubeINtoOUT is similar to transRGBINtoOUT but also looks up the +// colour cube index in a fourth table to yield a pixel value. + +void transRGBCubeINtoOUT (void* table, + const PixelFormat& inPF, void* inPtr, int inStride, + const PixelFormat& outPF, void* outPtr, + int outStride, int width, int height) +{ + OUTPIXEL* redTable = (OUTPIXEL*)table; + OUTPIXEL* greenTable = redTable + inPF.redMax + 1; + OUTPIXEL* blueTable = greenTable + inPF.greenMax + 1; + OUTPIXEL* cubeTable = blueTable + inPF.blueMax + 1; + INPIXEL* ip = (INPIXEL*)inPtr; + OUTPIXEL* op = (OUTPIXEL*)outPtr; + int inExtra = inStride - width; + int outExtra = outStride - width; + + while (height > 0) { + OUTPIXEL* opEndOfRow = op + width; + while (op < opEndOfRow) { + *op++ = cubeTable[(redTable [(*ip >> inPF.redShift) & inPF.redMax] + + greenTable[(*ip >> inPF.greenShift) & inPF.greenMax] + + blueTable [(*ip >> inPF.blueShift) & inPF.blueMax])]; + ip++; + } + ip += inExtra; + op += outExtra; + height--; + } +} + +#endif + +#undef INPIXEL +#undef OUTPIXEL +#undef transSimpleINtoOUT +#undef transRGBINtoOUT +#undef transRGBCubeINtoOUT diff --git a/common/rfb/util.cxx b/common/rfb/util.cxx new file mode 100644 index 00000000..57454324 --- /dev/null +++ b/common/rfb/util.cxx @@ -0,0 +1,184 @@ +/* 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. + */ + +/* + * The following applies to stcasecmp and strncasecmp implementations: + * + * Copyright (c) 1987 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of California at Berkeley. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific written prior permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +#include <rfb/util.h> + +// Provide strcasecmp() and/or strncasecmp() if absent on this system. + +#ifndef WIN32 +#if !defined(HAVE_STRCASECMP) || !defined(HAVE_STRNCASECMP) + +extern "C" { + +/* + * This array is designed for mapping upper and lower case letter + * together for a case independent comparison. The mappings are + * based upon ascii character sequences. + */ +static unsigned char s_charmap[] = { + '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007', + '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017', + '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027', + '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037', + '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047', + '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057', + '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067', + '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077', + '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147', + '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157', + '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167', + '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137', + '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147', + '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157', + '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167', + '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177', + '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207', + '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217', + '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227', + '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237', + '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247', + '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257', + '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267', + '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277', + '\300', '\341', '\342', '\343', '\344', '\345', '\346', '\347', + '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357', + '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367', + '\370', '\371', '\372', '\333', '\334', '\335', '\336', '\337', + '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347', + '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357', + '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367', + '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377', +}; + +#ifndef HAVE_STRCASECMP +int +strcasecmp(const char *s1, const char *s2) +{ + unsigned char u1, u2; + + for (;;) { + u1 = (unsigned char) *s1++; + u2 = (unsigned char) *s2++; + if (s_charmap[u1] != s_charmap[u2]) { + return s_charmap[u1] - s_charmap[u2]; + } + if (u1 == '\0') { + return 0; + } + } +} +#endif // !defined(HAVE_STRCASECMP) + +#ifndef HAVE_STRNCASECMP +int +strncasecmp(const char *s1, const char *s2, size_t n) +{ + unsigned char u1, u2; + + for (; n != 0; --n) { + u1 = (unsigned char) *s1++; + u2 = (unsigned char) *s2++; + if (s_charmap[u1] != s_charmap[u2]) { + return s_charmap[u1] - s_charmap[u2]; + } + if (u1 == '\0') { + return 0; + } + } + return 0; +} +#endif // !defined(HAVE_STRNCASECMP) + +} // extern "C" + +#endif // !defined(HAVE_STRCASECMP) || !defined(HAVE_STRNCASECMP) +#endif // defined(WIN32) + +namespace rfb { + + char* strDup(const char* s) { + if (!s) return 0; + int l = strlen(s); + char* r = new char[l+1]; + memcpy(r, s, l+1); + return r; + }; + + void strFree(char* s) { + delete [] s; + } + + + bool strSplit(const char* src, const char limiter, char** out1, char** out2, bool fromEnd) { + CharArray out1old, out2old; + if (out1) out1old.buf = *out1; + if (out2) out2old.buf = *out2; + int len = strlen(src); + int i=0, increment=1, limit=len; + if (fromEnd) { + i=len-1; increment = -1; limit = -1; + } + while (i!=limit) { + if (src[i] == limiter) { + if (out1) { + *out1 = new char[i+1]; + if (i) memcpy(*out1, src, i); + (*out1)[i] = 0; + } + if (out2) { + *out2 = new char[len-i]; + if (len-i-1) memcpy(*out2, &src[i+1], len-i-1); + (*out2)[len-i-1] = 0; + } + return true; + } + i+=increment; + } + if (out1) *out1 = strDup(src); + if (out2) *out2 = 0; + return false; + } + + bool strContains(const char* src, char c) { + int l=strlen(src); + for (int i=0; i<l; i++) + if (src[i] == c) return true; + return false; + } + + void strCopy(char* dest, const char* src, int destlen) { + if (src) + strncpy(dest, src, destlen-1); + dest[src ? destlen-1 : 0] = 0; + } + +}; diff --git a/common/rfb/util.h b/common/rfb/util.h new file mode 100644 index 00000000..fa205f08 --- /dev/null +++ b/common/rfb/util.h @@ -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. + */ + +// +// util.h - miscellaneous useful bits +// + +#ifndef __RFB_UTIL_H__ +#define __RFB_UTIL_H__ + +#include <limits.h> +#include <string.h> + +namespace rfb { + + // -=- Class to handle cleanup of arrays of characters + class CharArray { + public: + CharArray() : buf(0) {} + CharArray(char* str) : buf(str) {} // note: assumes ownership + CharArray(int len) { + buf = new char[len]; + } + ~CharArray() { + delete [] buf; + } + // Get the buffer pointer & clear it (i.e. caller takes ownership) + char* takeBuf() {char* tmp = buf; buf = 0; return tmp;} + void replaceBuf(char* b) {delete [] buf; buf = b;} + char* buf; + private: + CharArray(const CharArray&); + CharArray& operator=(const CharArray&); + }; + + char* strDup(const char* s); + void strFree(char* s); + + // Returns true if split successful. Returns false otherwise. + // ALWAYS *copies* first part of string to out1 buffer. + // If limiter not found, leaves out2 alone (null) and just copies to out1. + // If out1 or out2 non-zero, calls strFree and zeroes them. + // If fromEnd is true, splits at end of string rather than beginning. + // Either out1 or out2 may be null, in which case the split will not return + // that part of the string. Obviously, setting both to 0 is not useful... + bool strSplit(const char* src, const char limiter, char** out1, char** out2, bool fromEnd=false); + + // Returns true if src contains c + bool strContains(const char* src, char c); + + // Copies src to dest, up to specified length-1, and guarantees termination + void strCopy(char* dest, const char* src, int destlen); + + + // HELPER functions for timeout handling + + // soonestTimeout() is a function to help work out the soonest of several + // timeouts. + inline void soonestTimeout(int* timeout, int newTimeout) { + if (newTimeout && (!*timeout || newTimeout < *timeout)) + *timeout = newTimeout; + } + + // secsToMillis() turns seconds into milliseconds, capping the value so it + // can't wrap round and become -ve + inline int secsToMillis(int secs) { + return (secs < 0 || secs > (INT_MAX/1000) ? INT_MAX : secs * 1000); + } +} + +// 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 + +// Declare strcasecmp() and/or strncasecmp() if absent on this system. + +#if !defined(WIN32) && !defined(HAVE_STRCASECMP) +extern "C" { + int strcasecmp(const char *s1, const char *s2); +} +#endif +#if !defined(WIN32) && !defined(HAVE_STRNCASECMP) +extern "C" { + int strncasecmp(const char *s1, const char *s2, size_t n); +} +#endif + +#endif diff --git a/common/rfb/zrleDecode.h b/common/rfb/zrleDecode.h new file mode 100644 index 00000000..15d27900 --- /dev/null +++ b/common/rfb/zrleDecode.h @@ -0,0 +1,251 @@ +/* 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. + */ + +// +// ZRLE decoding function. +// +// This file is #included after having set the following macros: +// BPP - 8, 16 or 32 +// EXTRA_ARGS - optional extra arguments +// FILL_RECT - fill a rectangle with a single colour +// IMAGE_RECT - draw a rectangle of pixel data from a buffer + +#include <rdr/InStream.h> +#include <rdr/ZlibInStream.h> +#include <assert.h> + +namespace rfb { + +// CONCAT2E concatenates its arguments, expanding them if they are macros + +#ifndef CONCAT2E +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) +#endif + +#ifdef CPIXEL +#define PIXEL_T rdr::CONCAT2E(U,BPP) +#define READ_PIXEL CONCAT2E(readOpaque,CPIXEL) +#define ZRLE_DECODE CONCAT2E(zrleDecode,CPIXEL) +#else +#define PIXEL_T rdr::CONCAT2E(U,BPP) +#define READ_PIXEL CONCAT2E(readOpaque,BPP) +#define ZRLE_DECODE CONCAT2E(zrleDecode,BPP) +#endif + +void ZRLE_DECODE (const Rect& r, rdr::InStream* is, + rdr::ZlibInStream* zis, PIXEL_T* buf +#ifdef EXTRA_ARGS + , EXTRA_ARGS +#endif + ) +{ + int length = is->readU32(); + zis->setUnderlying(is, length); + Rect t; + + for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 64) { + + t.br.y = __rfbmin(r.br.y, t.tl.y + 64); + + for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 64) { + + t.br.x = __rfbmin(r.br.x, t.tl.x + 64); + + int mode = zis->readU8(); + bool rle = mode & 128; + int palSize = mode & 127; + PIXEL_T palette[128]; + + for (int i = 0; i < palSize; i++) { + palette[i] = zis->READ_PIXEL(); + } + + if (palSize == 1) { + PIXEL_T pix = palette[0]; + FILL_RECT(t,pix); + continue; + } + + if (!rle) { + if (palSize == 0) { + + // raw + +#ifdef CPIXEL + for (PIXEL_T* ptr = buf; ptr < buf+t.area(); ptr++) { + *ptr = zis->READ_PIXEL(); + } +#else + zis->readBytes(buf, t.area() * (BPP / 8)); +#endif + + } else { + + // packed pixels + int bppp = ((palSize > 16) ? 8 : + ((palSize > 4) ? 4 : ((palSize > 2) ? 2 : 1))); + + PIXEL_T* ptr = buf; + + for (int i = 0; i < t.height(); i++) { + PIXEL_T* eol = ptr + t.width(); + rdr::U8 byte = 0; + rdr::U8 nbits = 0; + + while (ptr < eol) { + if (nbits == 0) { + byte = zis->readU8(); + nbits = 8; + } + nbits -= bppp; + rdr::U8 index = (byte >> nbits) & ((1 << bppp) - 1) & 127; + *ptr++ = palette[index]; + } + } + } + +#ifdef FAVOUR_FILL_RECT + //fprintf(stderr,"copying data to screen %dx%d at %d,%d\n", + //t.width(),t.height(),t.tl.x,t.tl.y); + IMAGE_RECT(t,buf); +#endif + + } else { + + if (palSize == 0) { + + // plain RLE + + PIXEL_T* ptr = buf; + PIXEL_T* end = ptr + t.area(); + while (ptr < end) { + PIXEL_T pix = zis->READ_PIXEL(); + int len = 1; + int b; + do { + b = zis->readU8(); + len += b; + } while (b == 255); + + assert(len <= end - ptr); + +#ifdef FAVOUR_FILL_RECT + int i = ptr - buf; + ptr += len; + + int runX = i % t.width(); + int runY = i / t.width(); + + if (runX + len > t.width()) { + if (runX != 0) { + FILL_RECT(Rect(t.tl.x+runX, t.tl.y+runY, t.width()-runX, 1), + pix); + len -= t.width()-runX; + runX = 0; + runY++; + } + + if (len > t.width()) { + FILL_RECT(Rect(t.tl.x, t.tl.y+runY, t.width(), len/t.width()), + pix); + runY += len / t.width(); + len = len % t.width(); + } + } + + if (len != 0) { + FILL_RECT(Rect(t.tl.x+runX, t.tl.y+runY, len, 1), pix); + } +#else + while (len-- > 0) *ptr++ = pix; +#endif + + } + } else { + + // palette RLE + + PIXEL_T* ptr = buf; + PIXEL_T* end = ptr + t.area(); + while (ptr < end) { + int index = zis->readU8(); + int len = 1; + if (index & 128) { + int b; + do { + b = zis->readU8(); + len += b; + } while (b == 255); + + assert(len <= end - ptr); + } + + index &= 127; + + PIXEL_T pix = palette[index]; + +#ifdef FAVOUR_FILL_RECT + int i = ptr - buf; + ptr += len; + + int runX = i % t.width(); + int runY = i / t.width(); + + if (runX + len > t.width()) { + if (runX != 0) { + FILL_RECT(Rect(t.tl.x+runX, t.tl.y+runY, t.width()-runX, 1), + pix); + len -= t.width()-runX; + runX = 0; + runY++; + } + + if (len > t.width()) { + FILL_RECT(Rect(t.tl.x, t.tl.y+runY, t.width(), len/t.width()), + pix); + runY += len / t.width(); + len = len % t.width(); + } + } + + if (len != 0) { + FILL_RECT(Rect(t.tl.x+runX, t.tl.y+runY, len, 1), pix); + } +#else + while (len-- > 0) *ptr++ = pix; +#endif + } + } + } + +#ifndef FAVOUR_FILL_RECT + //fprintf(stderr,"copying data to screen %dx%d at %d,%d\n", + //t.width(),t.height(),t.tl.x,t.tl.y); + IMAGE_RECT(t,buf); +#endif + } + } + + zis->reset(); +} + +#undef ZRLE_DECODE +#undef READ_PIXEL +#undef PIXEL_T +} diff --git a/common/rfb/zrleEncode.h b/common/rfb/zrleEncode.h new file mode 100644 index 00000000..9b7263b3 --- /dev/null +++ b/common/rfb/zrleEncode.h @@ -0,0 +1,328 @@ +/* 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. + */ + +// +// zrleEncode.h - zrle encoding function. +// +// This file is #included after having set the following macros: +// BPP - 8, 16 or 32 +// EXTRA_ARGS - optional extra arguments +// GET_IMAGE_INTO_BUF - gets a rectangle of pixel data into a buffer +// +// Note that the buf argument to ZRLE_ENCODE needs to be at least one pixel +// bigger than the largest tile of pixel data, since the ZRLE encoding +// algorithm writes to the position one past the end of the pixel data. +// + +#include <rdr/OutStream.h> +#include <rdr/ZlibOutStream.h> +#include <assert.h> + +namespace rfb { + +// CONCAT2E concatenates its arguments, expanding them if they are macros + +#ifndef CONCAT2E +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) +#endif + +#ifdef CPIXEL +#define PIXEL_T rdr::CONCAT2E(U,BPP) +#define WRITE_PIXEL CONCAT2E(writeOpaque,CPIXEL) +#define ZRLE_ENCODE CONCAT2E(zrleEncode,CPIXEL) +#define ZRLE_ENCODE_TILE CONCAT2E(zrleEncodeTile,CPIXEL) +#define BPPOUT 24 +#else +#define PIXEL_T rdr::CONCAT2E(U,BPP) +#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP) +#define ZRLE_ENCODE CONCAT2E(zrleEncode,BPP) +#define ZRLE_ENCODE_TILE CONCAT2E(zrleEncodeTile,BPP) +#define BPPOUT BPP +#endif + +#ifndef ZRLE_ONCE +#define ZRLE_ONCE +static const int bitsPerPackedPixel[] = { + 0, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 +}; + +// The PaletteHelper class helps us build up the palette from pixel data by +// storing a reverse index using a simple hash-table + +class PaletteHelper { +public: + enum { MAX_SIZE = 127 }; + + PaletteHelper() + { + memset(index, 255, sizeof(index)); + size = 0; + } + + inline int hash(rdr::U32 pix) + { + return (pix ^ (pix >> 17)) & 4095; + } + + inline void insert(rdr::U32 pix) + { + if (size < MAX_SIZE) { + int i = hash(pix); + while (index[i] != 255 && key[i] != pix) + i++; + if (index[i] != 255) return; + + index[i] = size; + key[i] = pix; + palette[size] = pix; + } + size++; + } + + inline int lookup(rdr::U32 pix) + { + assert(size <= MAX_SIZE); + int i = hash(pix); + while (index[i] != 255 && key[i] != pix) + i++; + if (index[i] != 255) return index[i]; + return -1; + } + + rdr::U32 palette[MAX_SIZE]; + rdr::U8 index[4096+MAX_SIZE]; + rdr::U32 key[4096+MAX_SIZE]; + int size; +}; +#endif + +void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, rdr::OutStream* os); + +bool ZRLE_ENCODE (const Rect& r, rdr::OutStream* os, + rdr::ZlibOutStream* zos, void* buf, int maxLen, Rect* actual +#ifdef EXTRA_ARGS + , EXTRA_ARGS +#endif + ) +{ + zos->setUnderlying(os); + // RLE overhead is at worst 1 byte per 64x64 (4Kpixel) block + int worstCaseLine = r.width() * 64 * (BPPOUT/8) + 1 + r.width() / 64; + // Zlib overhead is at worst 6 bytes plus 5 bytes per 32Kbyte block. + worstCaseLine += 11 + 5 * (worstCaseLine >> 15); + Rect t; + + for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 64) { + + t.br.y = __rfbmin(r.br.y, t.tl.y + 64); + + if (os->length() + worstCaseLine > maxLen) { + if (t.tl.y == r.tl.y) + throw Exception("ZRLE: not enough space for first line?"); + actual->tl = r.tl; + actual->br.x = r.br.x; + actual->br.y = t.tl.y; + return false; + } + + for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 64) { + + t.br.x = __rfbmin(r.br.x, t.tl.x + 64); + + GET_IMAGE_INTO_BUF(t,buf); + + ZRLE_ENCODE_TILE((PIXEL_T*)buf, t.width(), t.height(), zos); + } + + zos->flush(); + } + return true; +} + + +void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, rdr::OutStream* os) +{ + // First find the palette and the number of runs + + PaletteHelper ph; + + int runs = 0; + int singlePixels = 0; + + PIXEL_T* ptr = data; + PIXEL_T* end = ptr + h * w; + *end = ~*(end-1); // one past the end is different so the while loop ends + + while (ptr < end) { + PIXEL_T pix = *ptr; + if (*++ptr != pix) { + singlePixels++; + } else { + while (*++ptr == pix) ; + runs++; + } + ph.insert(pix); + } + + //fprintf(stderr,"runs %d, single pixels %d, paletteSize %d\n", + // runs, singlePixels, ph.size); + + // Solid tile is a special case + + if (ph.size == 1) { + os->writeU8(1); + os->WRITE_PIXEL(ph.palette[0]); + return; + } + + // Try to work out whether to use RLE and/or a palette. We do this by + // estimating the number of bytes which will be generated and picking the + // method which results in the fewest bytes. Of course this may not result + // in the fewest bytes after compression... + + bool useRle = false; + bool usePalette = false; + + int estimatedBytes = w * h * (BPPOUT/8); // start assuming raw + + int plainRleBytes = ((BPPOUT/8)+1) * (runs + singlePixels); + + if (plainRleBytes < estimatedBytes) { + useRle = true; + estimatedBytes = plainRleBytes; + } + + if (ph.size < 128) { + int paletteRleBytes = (BPPOUT/8) * ph.size + 2 * runs + singlePixels; + + if (paletteRleBytes < estimatedBytes) { + useRle = true; + usePalette = true; + estimatedBytes = paletteRleBytes; + } + + if (ph.size < 17) { + int packedBytes = ((BPPOUT/8) * ph.size + + w * h * bitsPerPackedPixel[ph.size-1] / 8); + + if (packedBytes < estimatedBytes) { + useRle = false; + usePalette = true; + estimatedBytes = packedBytes; + } + } + } + + if (!usePalette) ph.size = 0; + + os->writeU8((useRle ? 128 : 0) | ph.size); + + for (int i = 0; i < ph.size; i++) { + os->WRITE_PIXEL(ph.palette[i]); + } + + if (useRle) { + + PIXEL_T* ptr = data; + PIXEL_T* end = ptr + w * h; + PIXEL_T* runStart; + PIXEL_T pix; + while (ptr < end) { + runStart = ptr; + pix = *ptr++; + while (*ptr == pix && ptr < end) + ptr++; + int len = ptr - runStart; + if (len <= 2 && usePalette) { + int index = ph.lookup(pix); + if (len == 2) + os->writeU8(index); + os->writeU8(index); + continue; + } + if (usePalette) { + int index = ph.lookup(pix); + os->writeU8(index | 128); + } else { + os->WRITE_PIXEL(pix); + } + len -= 1; + while (len >= 255) { + os->writeU8(255); + len -= 255; + } + os->writeU8(len); + } + + } else { + + // no RLE + + if (usePalette) { + + // packed pixels + + assert (ph.size < 17); + + int bppp = bitsPerPackedPixel[ph.size-1]; + + PIXEL_T* ptr = data; + + for (int i = 0; i < h; i++) { + rdr::U8 nbits = 0; + rdr::U8 byte = 0; + + PIXEL_T* eol = ptr + w; + + while (ptr < eol) { + PIXEL_T pix = *ptr++; + rdr::U8 index = ph.lookup(pix); + byte = (byte << bppp) | index; + nbits += bppp; + if (nbits >= 8) { + os->writeU8(byte); + nbits = 0; + } + } + if (nbits > 0) { + byte <<= 8 - nbits; + os->writeU8(byte); + } + } + } else { + + // raw + +#ifdef CPIXEL + for (PIXEL_T* ptr = data; ptr < data+w*h; ptr++) { + os->WRITE_PIXEL(*ptr); + } +#else + os->writeBytes(data, w*h*(BPP/8)); +#endif + } + } +} + +#undef PIXEL_T +#undef WRITE_PIXEL +#undef ZRLE_ENCODE +#undef ZRLE_ENCODE_TILE +#undef BPPOUT +} |